taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

commit 18f258f14217fdde4de5225eedddd6f2333f7c5a
parent ac92c9b8d0e1b5fda7a76cfaedbb343d44488799
Author: sebasjm+llm <sebasjm@numis.ar>
Date:   Sat,  3 Oct 2026 16:05:03 -0300

refactor: Each operation get its own file

Diffstat:
M_exts/typescriptdomain.py | 63++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mcore/api-wallet-core.rst | 329++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Dcore/wallet-core/balances.rst | 292-------------------------------------------------------------------------------
Acore/wallet-core/balances/get-balance-detail.rst | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/balances/get-balances.rst | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/bank-accounts.rst | 162-------------------------------------------------------------------------------
Acore/wallet-core/bank-accounts/add-bank-account.rst | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/bank-accounts/forget-bank-account.rst | 27+++++++++++++++++++++++++++
Acore/wallet-core/bank-accounts/get-bank-account-by-id.rst | 27+++++++++++++++++++++++++++
Acore/wallet-core/bank-accounts/list-bank-accounts.rst | 48++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/contacts.rst | 96-------------------------------------------------------------------------------
Acore/wallet-core/contacts/add-contact.rst | 48++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/contacts/delete-contact.rst | 30++++++++++++++++++++++++++++++
Acore/wallet-core/contacts/get-contacts.rst | 19+++++++++++++++++++
Dcore/wallet-core/database.rst | 239-------------------------------------------------------------------------------
Acore/wallet-core/database/clear-db.rst | 21+++++++++++++++++++++
Acore/wallet-core/database/export-db-to-file.rst | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/database/export-db.rst | 12++++++++++++
Acore/wallet-core/database/import-db-from-file.rst | 40++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/database/import-db.rst | 43+++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/database/migrate-database.rst | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/database/recycle.rst | 19+++++++++++++++++++
Dcore/wallet-core/deposits.rst | 421-------------------------------------------------------------------------------
Acore/wallet-core/deposits/check-deposit.rst | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/deposits/convert-deposit-amount.rst | 37+++++++++++++++++++++++++++++++++++++
Acore/wallet-core/deposits/create-deposit-group.rst | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/deposits/get-deposit-wire-types-for-currency.rst | 40++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/deposits/get-deposit-wire-types.rst | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/deposits/get-max-deposit-amount.rst | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/diagnostics.rst | 102-------------------------------------------------------------------------------
Acore/wallet-core/diagnostics/get-active-tasks.rst | 38++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/diagnostics/get-diagnostics.rst | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/donau.rst | 104-------------------------------------------------------------------------------
Acore/wallet-core/donau/get-donau-statements.rst | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/donau/get-donau.rst | 25+++++++++++++++++++++++++
Acore/wallet-core/donau/set-donau.rst | 31+++++++++++++++++++++++++++++++
Dcore/wallet-core/exchanges.rst | 940-------------------------------------------------------------------------------
Acore/wallet-core/exchanges/add-exchange.rst | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/complete-exchange-base-url.rst | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/confirm-exchange-key-change.rst | 44++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/delete-exchange.rst | 37+++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/get-default-exchanges.rst | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/get-exchange-detailed-info.rst | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/get-exchange-entry-by-url.rst | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/get-exchange-resources.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/get-exchange-tos.rst | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/list-exchanges.rst | 45+++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/list-withdrawal-exchange-candidates.rst | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/purge-exchange-legacy-keys.rst | 44++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/set-exchange-tos-accepted.rst | 27+++++++++++++++++++++++++++
Acore/wallet-core/exchanges/set-exchange-tos-forgotten.rst | 17+++++++++++++++++
Acore/wallet-core/exchanges/start-exchange-wallet-kyc.rst | 36++++++++++++++++++++++++++++++++++++
Acore/wallet-core/exchanges/update-exchange-entry.rst | 45+++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/global-currency.rst | 292-------------------------------------------------------------------------------
Acore/wallet-core/global-currency/add-global-currency-auditor.rst | 31+++++++++++++++++++++++++++++++
Acore/wallet-core/global-currency/add-global-currency-exchange.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/global-currency/get-currency-specification.rst | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/global-currency/list-global-currency-auditors.rst | 23+++++++++++++++++++++++
Acore/wallet-core/global-currency/list-global-currency-exchanges.rst | 23+++++++++++++++++++++++
Acore/wallet-core/global-currency/remove-global-currency-auditor.rst | 30++++++++++++++++++++++++++++++
Acore/wallet-core/global-currency/remove-global-currency-exchange.rst | 31+++++++++++++++++++++++++++++++
Dcore/wallet-core/hints.rst | 130-------------------------------------------------------------------------------
Acore/wallet-core/hints/dismiss-wallet-warning.rst | 35+++++++++++++++++++++++++++++++++++
Acore/wallet-core/hints/hint-application-resumed.rst | 32++++++++++++++++++++++++++++++++
Acore/wallet-core/hints/hint-network-availability.rst | 31+++++++++++++++++++++++++++++++
Acore/wallet-core/hints/hint-power-state.rst | 37+++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/init.rst | 196-------------------------------------------------------------------------------
Acore/wallet-core/init/get-version.rst | 40++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/init/init-wallet.rst | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/init/set-wallet-run-config.rst | 24++++++++++++++++++++++++
Acore/wallet-core/init/shutdown.rst | 23+++++++++++++++++++++++
Dcore/wallet-core/mailbox.rst | 260-------------------------------------------------------------------------------
Acore/wallet-core/mailbox/add-mailbox-message.rst | 25+++++++++++++++++++++++++
Acore/wallet-core/mailbox/delete-mailbox-message.rst | 30++++++++++++++++++++++++++++++
Acore/wallet-core/mailbox/get-mailbox-message.rst | 43+++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/mailbox/get-mailbox.rst | 29+++++++++++++++++++++++++++++
Acore/wallet-core/mailbox/initialize-mailbox.rst | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/mailbox/refresh-mailbox.rst | 41+++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/mailbox/send-taler-uri-mailbox-message.rst | 36++++++++++++++++++++++++++++++++++++
Mcore/wallet-core/notifications.rst | 14+++++++-------
Dcore/wallet-core/p2p.rst | 548-------------------------------------------------------------------------------
Acore/wallet-core/p2p/check-peer-pull-credit.rst | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/check-peer-push-debit-v2.rst | 42++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/check-peer-push-debit.rst | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/confirm-peer-pull-debit.rst | 37+++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/confirm-peer-push-credit.rst | 40++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/get-max-peer-push-debit-amount.rst | 44++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/initiate-peer-pull-credit.rst | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/initiate-peer-push-debit.rst | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/prepare-peer-pull-debit.rst | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/p2p/prepare-peer-push-credit.rst | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/payments.rst | 985-------------------------------------------------------------------------------
Acore/wallet-core/payments/check-pay-for-template.rst | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/confirm-pay.rst | 398+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/get-choices-for-payment.rst | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/get-paivana-cookie.rst | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/prepare-pay-for-paivana.rst | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/prepare-pay-for-template-v2.rst | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/prepare-pay-for-uri-v2.rst | 45+++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/reclaim-payment.rst | 31+++++++++++++++++++++++++++++++
Acore/wallet-core/payments/start-refund-query-for-uri.rst | 43+++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/payments/start-refund-query.rst | 26++++++++++++++++++++++++++
Acore/wallet-core/payments/unclaim-payment.rst | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/requests.rst | 65-----------------------------------------------------------------
Acore/wallet-core/requests/cancel-progress-token.rst | 30++++++++++++++++++++++++++++++
Acore/wallet-core/requests/retry-progress-token-now.rst | 38++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/taldir.rst | 152-------------------------------------------------------------------------------
Acore/wallet-core/taldir/complete-register-alias.rst | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/taldir/lookup-alias.rst | 43+++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/taldir/register-alias.rst | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/testing.rst | 1060-------------------------------------------------------------------------------
Acore/wallet-core/testing/apply-dev-experiment.rst | 25+++++++++++++++++++++++++
Acore/wallet-core/testing/dump-coins.rst | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/force-refresh.rst | 39+++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/run-integration-test-v2.rst | 28++++++++++++++++++++++++++++
Acore/wallet-core/testing/run-integration-test.rst | 29+++++++++++++++++++++++++++++
Acore/wallet-core/testing/set-coin-suspended.rst | 24++++++++++++++++++++++++
Acore/wallet-core/testing/test-crypto.rst | 12++++++++++++
Acore/wallet-core/testing/test-pay.rst | 45+++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-check-coins.rst | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-corrupt-withdrawal-coin-sel.rst | 31+++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-get-db-stats.rst | 13+++++++++++++
Acore/wallet-core/testing/testing-get-denom-stats.rst | 32++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-get-flight-records.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-get-performance-stats.rst | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-get-reserve-history.rst | 25+++++++++++++++++++++++++
Acore/wallet-core/testing/testing-get-sample-transactions.rst | 13+++++++++++++
Acore/wallet-core/testing/testing-ping.rst | 13+++++++++++++
Acore/wallet-core/testing/testing-plan-migrate-exchange-base-url.rst | 26++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-recover-coins.rst | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-reset-all-retries.rst | 17+++++++++++++++++
Acore/wallet-core/testing/testing-run-fixup.rst | 23+++++++++++++++++++++++
Acore/wallet-core/testing/testing-set-timetravel.rst | 23+++++++++++++++++++++++
Acore/wallet-core/testing/testing-wait-balance.rst | 23+++++++++++++++++++++++
Acore/wallet-core/testing/testing-wait-exchange-ready.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-wait-exchange-state.rst | 31+++++++++++++++++++++++++++++++
Acore/wallet-core/testing/testing-wait-refreshes-final.rst | 14++++++++++++++
Acore/wallet-core/testing/testing-wait-tasks-done.rst | 14++++++++++++++
Acore/wallet-core/testing/testing-wait-transaction-state.rst | 19+++++++++++++++++++
Acore/wallet-core/testing/testing-wait-transactions-final.rst | 14++++++++++++++
Acore/wallet-core/testing/testing-wait-wallet-kyc.rst | 28++++++++++++++++++++++++++++
Acore/wallet-core/testing/withdraw-test-balance.rst | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/testing/withdraw-testkudos.rst | 28++++++++++++++++++++++++++++
Dcore/wallet-core/tokens.rst | 185-------------------------------------------------------------------------------
Acore/wallet-core/tokens/delete-discount.rst | 29+++++++++++++++++++++++++++++
Acore/wallet-core/tokens/delete-subscription.rst | 29+++++++++++++++++++++++++++++
Acore/wallet-core/tokens/list-discounts.rst | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/tokens/list-subscriptions.rst | 30++++++++++++++++++++++++++++++
Dcore/wallet-core/transactions.rst | 1380-------------------------------------------------------------------------------
Acore/wallet-core/transactions/abort-transaction.rst | 38++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/delete-transaction.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/fail-transaction.rst | 39+++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/get-transaction-by-id.rst | 741+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/get-transactions-v2.rst | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/get-transactions.rst | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/list-associated-refreshes.rst | 28++++++++++++++++++++++++++++
Acore/wallet-core/transactions/resolve-transaction-reference.rst | 48++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/resume-transaction.rst | 24++++++++++++++++++++++++
Acore/wallet-core/transactions/retry-transaction.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/transactions/suspend-transaction.rst | 25+++++++++++++++++++++++++
Acore/wallet-core/transactions/wait-transaction-state.rst | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcore/wallet-core/validation.rst | 230-------------------------------------------------------------------------------
Acore/wallet-core/validation/canonicalize-base-url.rst | 32++++++++++++++++++++++++++++++++
Acore/wallet-core/validation/convert-iban-account-field-to-payto.rst | 36++++++++++++++++++++++++++++++++++++
Acore/wallet-core/validation/convert-iban-payto-to-account-field.rst | 34++++++++++++++++++++++++++++++++++
Acore/wallet-core/validation/get-banking-choices-for-payto.rst | 42++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/validation/get-qr-codes-for-payto.rst | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/validation/validate-iban.rst | 25+++++++++++++++++++++++++
Dcore/wallet-core/withdrawals.rst | 625-------------------------------------------------------------------------------
Acore/wallet-core/withdrawals/accept-bank-integrated-withdrawal.rst | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/withdrawals/accept-manual-withdrawal.rst | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/withdrawals/confirm-withdrawal.rst | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/withdrawals/get-withdrawal-details-for-amount.rst | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/withdrawals/get-withdrawal-details-for-uri.rst | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/withdrawals/prepare-bank-integrated-withdrawal.rst | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acore/wallet-core/withdrawals/prepare-withdraw-exchange.rst | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
176 files changed, 8776 insertions(+), 8501 deletions(-)

diff --git a/_exts/typescriptdomain.py b/_exts/typescriptdomain.py @@ -30,8 +30,9 @@ from pygments.formatters import HtmlFormatter from docutils.nodes import Element, Node from sphinx.roles import XRefRole +from sphinx import addnodes from sphinx.domains import Domain, ObjType -from sphinx.directives import directives +from sphinx.directives import ObjectDescription, directives from sphinx.directives.code import ( container_wrapper, dedent_lines, @@ -126,6 +127,60 @@ class TypeScriptDefinition(SphinxDirective): return [literal] +class TypeScriptOperation(ObjectDescription): + """ + Directive for an operation of an operation-based, non-HTTP API + (such as the wallet-core client API). + + Renders like an endpoint from sphinxcontrib.httpdomain and registers + the operation as a target for the ``ts:op`` cross-reference role: + + .. ts:op:: getBalances + :read-only: + + Description of the operation ... + + Note that options and content must share the same indentation; + docutils misreads an option line that is indented deeper than + the content. + """ + + doc_field_types = [] + + option_spec = { + "read-only": directives.flag, + "deprecated": directives.flag, + "noindex": directives.flag, + } + + index_label = "wallet-core operation" + + def handle_signature(self, sig, signode): + signode += addnodes.desc_name(sig, sig) + if "read-only" in self.options: + signode += addnodes.desc_annotation("read-only", "read-only") + if "deprecated" in self.options: + signode += addnodes.desc_annotation("deprecated", "deprecated") + signode["fullname"] = sig + return sig + + def needs_arglist(self): + return False + + def add_target_and_index(self, name, sig, signode): + tsid = "tsref-op-" + name + signode["ids"].append(tsid) + ts = self.env.get_domain("ts") + ts.add_object("op", name, self.env.docname, tsid) + if "noindex" not in self.options: + self.indexnode["entries"].append( + ("single", "%s (%s)" % (name, self.index_label), tsid, "", None) + ) + + def get_index_text(self, modname, name): + return "" + + class TypeScriptDomain(Domain): """TypeScript domain.""" @@ -133,6 +188,7 @@ class TypeScriptDomain(Domain): label = "TypeScript" object_types = { "type": ObjType("type", "type"), + "op": ObjType("op", "op"), } initial_data = { "objects": {}, @@ -140,16 +196,21 @@ class TypeScriptDomain(Domain): directives = { "def": TypeScriptDefinition, + "op": TypeScriptOperation, } roles = { "type": XRefRole( lowercase=False, warn_dangling=True, innernodeclass=nodes.inline ), + "op": XRefRole( + lowercase=False, warn_dangling=True, innernodeclass=nodes.inline + ), } dangling_warnings = { "type": "undefined TypeScript type: %(target)s", + "op": "undefined operation: %(target)s", } def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): diff --git a/core/api-wallet-core.rst b/core/api-wallet-core.rst @@ -50,7 +50,7 @@ Version History The wallet-core API is versioned using the :ref:`libtool version range format <http-common>` (``current[:revision[:age]]``). The currently implemented protocol version is **10:0:0**, reported via the -:ref:`getVersion <wallet-op-getVersion>` operation. +:ts:op:`getVersion` operation. **Version history:** @@ -60,10 +60,10 @@ implemented protocol version is **10:0:0**, reported via the * ``v7``: introduces the transaction finalizing state * ``v8``: removes the v1 ``preparePay`` operations * ``v9``: payments can be handed off to another wallet - (:ref:`unclaimPayment <wallet-op-unclaimPayment>` and - :ref:`reclaimPayment <wallet-op-reclaimPayment>`) + (:ts:op:`unclaimPayment` and + :ts:op:`reclaimPayment`) * ``v10``: privacy-scrubbed diagnostics reports - (:ref:`getDiagnostics <wallet-op-getDiagnostics>`) + (:ts:op:`getDiagnostics`) .. _wallet-core-conventions: @@ -169,11 +169,11 @@ via the error envelope, but is not listed per operation. Initialization ^^^^^^^^^^^^^^ -:ref:`initWallet <wallet-op-initWallet>` (or -:ref:`setWalletRunConfig <wallet-op-setWalletRunConfig>`) must be the first +:ts:op:`initWallet` (or +:ts:op:`setWalletRunConfig`) must be the first request made to wallet-core; every other operation fails until initialization has completed. Once the wallet has been shut down via -:ref:`shutdown <wallet-op-shutdown>`, every operation other than +:ts:op:`shutdown`, every operation other than ``shutdown`` itself fails with ``WALLET_CORE_NOT_AVAILABLE`` until wallet-core is restarted and initialized again. @@ -250,15 +250,30 @@ notification envelopes are wrapped in a versioned browser RPC message; see Operations ---------- -The operations are grouped by topic. Unless noted otherwise, each +The operations are grouped by topic. Each operation is introduced by a +signature line with the operation's name, as sent in the ``operation`` +field of the `CoreApiRequestEnvelope`. Unless noted otherwise, the operation's ``args`` must be an object of the stated request type, and a successful ``result`` is an object of the stated response type. +Operations annotated *read-only* are pure functions of the wallet's +stored state: they perform no database writes, do not create, cancel or +otherwise affect transactions or background tasks, emit no notifications +and perform no network requests. All other operations document their +side effects explicitly. Operations annotated *deprecated* are kept for +backwards compatibility and should not be used by new clients. + Initialization and lifecycle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. include:: wallet-core/init.rst +.. include:: wallet-core/init/init-wallet.rst + +.. include:: wallet-core/init/set-wallet-run-config.rst + +.. include:: wallet-core/init/get-version.rst + +.. include:: wallet-core/init/shutdown.rst Generic request management ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -266,7 +281,9 @@ Generic request management Long-running operations that expose a ``progressToken`` can be cancelled or nudged with the following requests. -.. include:: wallet-core/requests.rst +.. include:: wallet-core/requests/retry-progress-token-now.rst + +.. include:: wallet-core/requests/cancel-progress-token.rst Hints ^^^^^ @@ -274,12 +291,20 @@ Hints Hints inform wallet-core about the state of the host application. They do not query information and never return a meaningful result. -.. include:: wallet-core/hints.rst +.. include:: wallet-core/hints/hint-network-availability.rst + +.. include:: wallet-core/hints/hint-power-state.rst + +.. include:: wallet-core/hints/dismiss-wallet-warning.rst + +.. include:: wallet-core/hints/hint-application-resumed.rst Balances ^^^^^^^^ -.. include:: wallet-core/balances.rst +.. include:: wallet-core/balances/get-balances.rst + +.. include:: wallet-core/balances/get-balance-detail.rst Transactions ^^^^^^^^^^^^ @@ -290,7 +315,29 @@ refreshes, peer-to-peer transfers, deposits, ...) is represented as a via :ref:`transaction-state-transition <wallet-notif-transaction-state-transition>` notifications. -.. include:: wallet-core/transactions.rst +.. include:: wallet-core/transactions/get-transactions.rst + +.. include:: wallet-core/transactions/get-transactions-v2.rst + +.. include:: wallet-core/transactions/get-transaction-by-id.rst + +.. include:: wallet-core/transactions/resolve-transaction-reference.rst + +.. include:: wallet-core/transactions/abort-transaction.rst + +.. include:: wallet-core/transactions/fail-transaction.rst + +.. include:: wallet-core/transactions/suspend-transaction.rst + +.. include:: wallet-core/transactions/resume-transaction.rst + +.. include:: wallet-core/transactions/delete-transaction.rst + +.. include:: wallet-core/transactions/retry-transaction.rst + +.. include:: wallet-core/transactions/list-associated-refreshes.rst + +.. include:: wallet-core/transactions/wait-transaction-state.rst Withdrawals ^^^^^^^^^^^ @@ -299,7 +346,19 @@ Withdrawal operations bring coins from an exchange into the wallet, either via a bank-integrated flow (the user authorizes the transfer in their banking application) or via a manual wire transfer to the exchange. -.. include:: wallet-core/withdrawals.rst +.. include:: wallet-core/withdrawals/prepare-withdraw-exchange.rst + +.. include:: wallet-core/withdrawals/prepare-bank-integrated-withdrawal.rst + +.. include:: wallet-core/withdrawals/confirm-withdrawal.rst + +.. include:: wallet-core/withdrawals/accept-bank-integrated-withdrawal.rst + +.. include:: wallet-core/withdrawals/get-withdrawal-details-for-amount.rst + +.. include:: wallet-core/withdrawals/accept-manual-withdrawal.rst + +.. include:: wallet-core/withdrawals/get-withdrawal-details-for-uri.rst Merchant payments ^^^^^^^^^^^^^^^^^ @@ -308,7 +367,27 @@ Payment operations handle ``taler://pay/`` and ``taler://pay-template/`` URIs as well as payments to Paivana-protected resources, and follow-up actions such as refund queries and payment hand-off between wallets. -.. include:: wallet-core/payments.rst +.. include:: wallet-core/payments/get-choices-for-payment.rst + +.. include:: wallet-core/payments/prepare-pay-for-uri-v2.rst + +.. include:: wallet-core/payments/prepare-pay-for-template-v2.rst + +.. include:: wallet-core/payments/prepare-pay-for-paivana.rst + +.. include:: wallet-core/payments/get-paivana-cookie.rst + +.. include:: wallet-core/payments/unclaim-payment.rst + +.. include:: wallet-core/payments/reclaim-payment.rst + +.. include:: wallet-core/payments/check-pay-for-template.rst + +.. include:: wallet-core/payments/start-refund-query-for-uri.rst + +.. include:: wallet-core/payments/start-refund-query.rst + +.. include:: wallet-core/payments/confirm-pay.rst Deposits ^^^^^^^^ @@ -316,7 +395,17 @@ Deposits Deposit operations send coins from the wallet to a bank account, usually the wallet user's own account. -.. include:: wallet-core/deposits.rst +.. include:: wallet-core/deposits/check-deposit.rst + +.. include:: wallet-core/deposits/create-deposit-group.rst + +.. include:: wallet-core/deposits/convert-deposit-amount.rst + +.. include:: wallet-core/deposits/get-max-deposit-amount.rst + +.. include:: wallet-core/deposits/get-deposit-wire-types.rst + +.. include:: wallet-core/deposits/get-deposit-wire-types-for-currency.rst Peer-to-peer payments ^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +414,25 @@ Peer-to-peer payments move funds directly between two wallets. In a *push* payment, the sender initiates the transfer; in a *pull* payment, the receiver requests to be paid by the sender. -.. include:: wallet-core/p2p.rst +.. include:: wallet-core/p2p/prepare-peer-push-credit.rst + +.. include:: wallet-core/p2p/check-peer-push-debit.rst + +.. include:: wallet-core/p2p/check-peer-push-debit-v2.rst + +.. include:: wallet-core/p2p/initiate-peer-push-debit.rst + +.. include:: wallet-core/p2p/confirm-peer-push-credit.rst + +.. include:: wallet-core/p2p/check-peer-pull-credit.rst + +.. include:: wallet-core/p2p/initiate-peer-pull-credit.rst + +.. include:: wallet-core/p2p/prepare-peer-pull-debit.rst + +.. include:: wallet-core/p2p/confirm-peer-pull-debit.rst + +.. include:: wallet-core/p2p/get-max-peer-push-debit-amount.rst Exchange management ^^^^^^^^^^^^^^^^^^^ @@ -333,7 +440,37 @@ Exchange management These operations manage the set of exchanges known to the wallet, their terms of service and their key material. -.. include:: wallet-core/exchanges.rst +.. include:: wallet-core/exchanges/add-exchange.rst + +.. include:: wallet-core/exchanges/list-exchanges.rst + +.. include:: wallet-core/exchanges/list-withdrawal-exchange-candidates.rst + +.. include:: wallet-core/exchanges/get-default-exchanges.rst + +.. include:: wallet-core/exchanges/get-exchange-entry-by-url.rst + +.. include:: wallet-core/exchanges/update-exchange-entry.rst + +.. include:: wallet-core/exchanges/get-exchange-resources.rst + +.. include:: wallet-core/exchanges/complete-exchange-base-url.rst + +.. include:: wallet-core/exchanges/delete-exchange.rst + +.. include:: wallet-core/exchanges/purge-exchange-legacy-keys.rst + +.. include:: wallet-core/exchanges/confirm-exchange-key-change.rst + +.. include:: wallet-core/exchanges/set-exchange-tos-accepted.rst + +.. include:: wallet-core/exchanges/set-exchange-tos-forgotten.rst + +.. include:: wallet-core/exchanges/get-exchange-tos.rst + +.. include:: wallet-core/exchanges/get-exchange-detailed-info.rst + +.. include:: wallet-core/exchanges/start-exchange-wallet-kyc.rst Bank accounts ^^^^^^^^^^^^^ @@ -341,7 +478,13 @@ Bank accounts Bank accounts known to the wallet are used as targets for deposits and as a fallback when entering peer-to-peer payment information manually. -.. include:: wallet-core/bank-accounts.rst +.. include:: wallet-core/bank-accounts/list-bank-accounts.rst + +.. include:: wallet-core/bank-accounts/get-bank-account-by-id.rst + +.. include:: wallet-core/bank-accounts/add-bank-account.rst + +.. include:: wallet-core/bank-accounts/forget-bank-account.rst Global currency management ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +493,19 @@ These operations manage the wallet's configuration for a global currency: the auditors that the wallet trusts for a currency and the exchanges offering it, as well as the currency specification itself. -.. include:: wallet-core/global-currency.rst +.. include:: wallet-core/global-currency/list-global-currency-exchanges.rst + +.. include:: wallet-core/global-currency/list-global-currency-auditors.rst + +.. include:: wallet-core/global-currency/add-global-currency-exchange.rst + +.. include:: wallet-core/global-currency/remove-global-currency-exchange.rst + +.. include:: wallet-core/global-currency/add-global-currency-auditor.rst + +.. include:: wallet-core/global-currency/remove-global-currency-auditor.rst + +.. include:: wallet-core/global-currency/get-currency-specification.rst Tokens ^^^^^^ @@ -358,7 +513,13 @@ Tokens Token families represent discount tokens and subscription tokens obtained during merchant payments. -.. include:: wallet-core/tokens.rst +.. include:: wallet-core/tokens/list-discounts.rst + +.. include:: wallet-core/tokens/delete-discount.rst + +.. include:: wallet-core/tokens/list-subscriptions.rst + +.. include:: wallet-core/tokens/delete-subscription.rst Donau ^^^^^ @@ -366,12 +527,20 @@ Donau The *donau* (donation authority) collects donation receipts for the wallet user. These operations configure the donau and query donation statements. -.. include:: wallet-core/donau.rst +.. include:: wallet-core/donau/set-donau.rst + +.. include:: wallet-core/donau/get-donau.rst + +.. include:: wallet-core/donau/get-donau-statements.rst Contacts ^^^^^^^^ -.. include:: wallet-core/contacts.rst +.. include:: wallet-core/contacts/add-contact.rst + +.. include:: wallet-core/contacts/delete-contact.rst + +.. include:: wallet-core/contacts/get-contacts.rst Mailbox ^^^^^^^ @@ -379,7 +548,19 @@ Mailbox The mailbox is used to receive ``taler://`` URIs (for example payment requests) from other wallet users. -.. include:: wallet-core/mailbox.rst +.. include:: wallet-core/mailbox/get-mailbox.rst + +.. include:: wallet-core/mailbox/initialize-mailbox.rst + +.. include:: wallet-core/mailbox/get-mailbox-message.rst + +.. include:: wallet-core/mailbox/add-mailbox-message.rst + +.. include:: wallet-core/mailbox/delete-mailbox-message.rst + +.. include:: wallet-core/mailbox/send-taler-uri-mailbox-message.rst + +.. include:: wallet-core/mailbox/refresh-mailbox.rst Aliases (taldir) ^^^^^^^^^^^^^^^^ @@ -388,14 +569,30 @@ Aliases map human-readable identifiers to wallet addresses via a *taldir* service, allowing senders to pay the wallet user without exchanging URIs out of band. -.. include:: wallet-core/taldir.rst +.. include:: wallet-core/taldir/register-alias.rst + +.. include:: wallet-core/taldir/complete-register-alias.rst + +.. include:: wallet-core/taldir/lookup-alias.rst Database management ^^^^^^^^^^^^^^^^^^^ These operations back up, restore, migrate and clear the wallet database. -.. include:: wallet-core/database.rst +.. include:: wallet-core/database/import-db.rst + +.. include:: wallet-core/database/export-db.rst + +.. include:: wallet-core/database/export-db-to-file.rst + +.. include:: wallet-core/database/import-db-from-file.rst + +.. include:: wallet-core/database/migrate-database.rst + +.. include:: wallet-core/database/clear-db.rst + +.. include:: wallet-core/database/recycle.rst Data validation and conversion ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -403,12 +600,24 @@ Data validation and conversion Utility operations to validate and convert between data formats used by the front-ends. -.. include:: wallet-core/validation.rst +.. include:: wallet-core/validation/validate-iban.rst + +.. include:: wallet-core/validation/canonicalize-base-url.rst + +.. include:: wallet-core/validation/convert-iban-account-field-to-payto.rst + +.. include:: wallet-core/validation/convert-iban-payto-to-account-field.rst + +.. include:: wallet-core/validation/get-banking-choices-for-payto.rst + +.. include:: wallet-core/validation/get-qr-codes-for-payto.rst Diagnostics ^^^^^^^^^^^ -.. include:: wallet-core/diagnostics.rst +.. include:: wallet-core/diagnostics/get-diagnostics.rst + +.. include:: wallet-core/diagnostics/get-active-tasks.rst Testing and debugging ^^^^^^^^^^^^^^^^^^^^^ @@ -417,7 +626,69 @@ These operations are only meant for integration tests and developer experiments. They are not part of the API surface a production front-end should rely on. -.. include:: wallet-core/testing.rst +.. include:: wallet-core/testing/apply-dev-experiment.rst + +.. include:: wallet-core/testing/testing-get-sample-transactions.rst + +.. include:: wallet-core/testing/withdraw-testkudos.rst + +.. include:: wallet-core/testing/withdraw-test-balance.rst + +.. include:: wallet-core/testing/run-integration-test.rst + +.. include:: wallet-core/testing/run-integration-test-v2.rst + +.. include:: wallet-core/testing/dump-coins.rst + +.. include:: wallet-core/testing/test-crypto.rst + +.. include:: wallet-core/testing/test-pay.rst + +.. include:: wallet-core/testing/set-coin-suspended.rst + +.. include:: wallet-core/testing/force-refresh.rst + +.. include:: wallet-core/testing/testing-wait-transactions-final.rst + +.. include:: wallet-core/testing/testing-wait-refreshes-final.rst + +.. include:: wallet-core/testing/testing-wait-transaction-state.rst + +.. include:: wallet-core/testing/testing-wait-exchange-state.rst + +.. include:: wallet-core/testing/testing-wait-exchange-ready.rst + +.. include:: wallet-core/testing/testing-wait-tasks-done.rst + +.. include:: wallet-core/testing/testing-wait-balance.rst + +.. include:: wallet-core/testing/testing-get-db-stats.rst + +.. include:: wallet-core/testing/testing-set-timetravel.rst + +.. include:: wallet-core/testing/testing-get-denom-stats.rst + +.. include:: wallet-core/testing/testing-recover-coins.rst + +.. include:: wallet-core/testing/testing-check-coins.rst + +.. include:: wallet-core/testing/testing-ping.rst + +.. include:: wallet-core/testing/testing-get-reserve-history.rst + +.. include:: wallet-core/testing/testing-reset-all-retries.rst + +.. include:: wallet-core/testing/testing-wait-wallet-kyc.rst + +.. include:: wallet-core/testing/testing-plan-migrate-exchange-base-url.rst + +.. include:: wallet-core/testing/testing-run-fixup.rst + +.. include:: wallet-core/testing/testing-get-flight-records.rst + +.. include:: wallet-core/testing/testing-get-performance-stats.rst + +.. include:: wallet-core/testing/testing-corrupt-withdrawal-coin-sel.rst .. _wallet-core-notifications: diff --git a/core/wallet-core/balances.rst b/core/wallet-core/balances.rst @@ -1,292 +0,0 @@ -.. _wallet-op-getBalances: - -**getBalances** - -Get current wallet balance. - -Balances are reported per *scope* (`ScopeInfo`): funds held globally for -a currency, at a particular exchange, under a particular auditor or under -a superseded exchange master key are reported as separate balances. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `BalancesResponse` object. - -**Details:** - - Each balance distinguishes three amounts. ``available`` is the - balance available for spending from transactions in their final state, - plus amounts expected to become available from pending refreshes. - ``pendingIncoming`` is the expected positive delta to the available - balance once pending operations (such as withdrawals or incoming peer - payments) reach the "done" state. ``pendingOutgoing`` is the amount - currently allocated to spend operations that could still be aborted, - in which case part of the amount may be recovered. - -.. ts:def:: BalancesResponse - - interface BalancesResponse { - // Electronic cash balances, per currency scope. - balances: WalletBalance[]; - - // Does the user have money from an exchange other than demo or test? - haveProdBalance: boolean; - - // Summary of donations, per donau/year/currency. - donauSummary?: DonauSummaryItem[]; - } - -.. ts:def:: WalletBalance - - interface WalletBalance { - // DD71 expiry information and conditional - // cost of keeping this balance. - refreshInfo?: WalletRefreshInfo; - - // Scope of the funds covered by this balance. - scopeInfo: ScopeInfo; - - // Balance available for spending, including amounts - // expected from pending refreshes. - available: AmountString; - - // Expected positive delta to the available balance - // from pending operations. - pendingIncoming: AmountString; - - // Amount allocated to spend operations that could still be aborted. - pendingOutgoing: AmountString; - - // Pending KYC or confirmation steps affecting this balance. - flags: BalanceFlag[]; - - // Available URLs for pages that list - // where money in this scope can be spent. - shoppingUrls?: string[]; - - // Are p2p payments disabled for this scope? - disablePeerPayments?: boolean; - - // Are wallet deposits disabled for this scope? - disableDirectDeposits?: boolean; - } - -.. ts:def:: WalletRefreshInfo - - interface WalletRefreshInfo { - risks: CashExpirationRisk[]; - recoveries: CashRenewalNotice[]; - annualCostBound: AnnualRefreshCostBound; - } - -.. ts:def:: CashExpirationRisk - - interface CashExpirationRisk { - exchangeBaseUrl: string; - exchangeMasterPub: string; - amount: AmountString; - earliestDepositExpiration: TalerProtocolTimestamp; - reason: - | "pending" - | "connectivity" - | "exchange-error" - | "no-replacement" - | "checking" - | "invalid-lifetime"; - } - -.. ts:def:: CashRenewalNotice - - interface CashRenewalNotice { - warningId: string; - exchangeBaseUrl: string; - exchangeMasterPub: string; - amount: AmountString; - oldDepositExpiration: TalerProtocolTimestamp; - newDepositExpiration: TalerProtocolTimestamp; - // Earliest emergency threshold of the renewed coins. - nextRelevantDate: TalerProtocolTimestamp; - } - -.. ts:def:: AnnualRefreshCostBound - - // Conditional on stable, continuously available compatible - // offerings and timely reveal. - type AnnualRefreshCostBound = { - horizonDays: 365; - projection: "stable-current-offerings"; - } & ( - | { - status: "available"; - amount: AmountString; - } - | { - status: "unavailable"; - reasons: string[]; - } - ); - -.. ts:def:: ScopeInfo - - // Scope of a balance; identifies the trust domain - // the funds belong to. - type ScopeInfo = - | ScopeInfoGlobal - | ScopeInfoExchange - | ScopeInfoAuditor - | ScopeInfoExchangeLegacyKeys; - -.. ts:def:: ScopeInfoGlobal - - // Funds held with an exchange that is globally - // trusted for the currency. - type ScopeInfoGlobal = { - type: "global"; - currency: string; - }; - -.. ts:def:: ScopeInfoExchange - - // Funds held at one particular exchange. - type ScopeInfoExchange = { - type: "exchange"; - currency: string; - url: string; - }; - -.. ts:def:: ScopeInfoAuditor - - // Funds whose denominations are audited by a globally - // trusted auditor. - type ScopeInfoAuditor = { - type: "auditor"; - currency: string; - url: string; - }; - -.. ts:def:: ScopeInfoExchangeLegacyKeys - - // Funds issued under a master public key that the exchange has - // since replaced; never pooled with funds under the current key. - type ScopeInfoExchangeLegacyKeys = { - type: "exchange-legacy-keys"; - currency: string; - url: string; - // The superseded key the funds were issued under. - masterPub: string; - }; - -.. ts:def:: BalanceFlag - - // Flag marking a pending KYC, AML or confirmation step for - // the incoming or outgoing funds of a balance. - type BalanceFlag = - | "incoming-kyc" - | "incoming-aml" - | "incoming-confirmation" - | "outgoing-kyc"; - -.. ts:def:: DonauSummaryItem - - interface DonauSummaryItem { - // Base URL of the donau service. - donauBaseUrl: string; - - // Legal domain of the donau service (if available). - legalDomain?: string; - - // Year of the donation(s). - year: number; - - // Sum of donation receipts received from merchants - // in the applicable year. - amountReceiptsAvailable: AmountString; - - // Sum of donation receipts already submitted to the - // donau in the applicable year. - amountReceiptsSubmitted: AmountString; - - // Amount of the latest available statement. Missing - // if no statement was requested yet. - amountStatement?: AmountString; - } - - -.. _wallet-op-getBalanceDetail: - -**getBalanceDetail** - -Get detailed balance information for one currency. - -Unlike :ref:`getBalances <wallet-op-getBalances>`, which reports one -balance per scope, this operation aggregates the wallet's funds in the -given currency across all exchanges known to the wallet and reports how -much of the balance is spendable under progressively stricter criteria. - -**Request:** - - The request ``args`` must be a `GetBalanceDetailRequest` object. - -**Response:** - - On success, the result is a `PaymentBalanceDetails` object. - -**Details:** - - ``balanceAvailable`` covers funds available for spending, including - amounts expected from pending refreshes. ``balanceMaterial`` is the - balance the wallet believes it could spend right now, without waiting - for any operations to complete. The remaining balances are subsets - of the material balance: ``balanceAgeAcceptable`` applies an age - restriction, the ``balanceReceiver*Acceptable`` balances restrict to - funds that a receiver accepts based on exchange URL, exchange public - key or auditor URL, and the depositable balances additionally require - that the funds can be deposited via a supported wire method. - -.. ts:def:: GetBalanceDetailRequest - - interface GetBalanceDetailRequest { - // Currency to compute the balance details for. - currency: string; - } - -.. ts:def:: PaymentBalanceDetails - - interface PaymentBalanceDetails { - // Balance of type "available" (see details above). - balanceAvailable: AmountJson; - - // Balance of type "material" (see details above). - balanceMaterial: AmountJson; - - // Balance of type "age-acceptable" (see details above). - balanceAgeAcceptable: AmountJson; - - // Balance of type "receiver-acceptable" (see details above). - // Deprecated, use the balanceReceiver*Acceptable balances instead. - balanceReceiverAcceptable: AmountJson; - - // Balance of type "receiver-exchange-url-acceptable". - balanceReceiverExchangeUrlAcceptable: AmountJson; - - // Balance of type "receiver-exchange-pub-acceptable". - balanceReceiverExchangePubAcceptable: AmountJson; - - // Balance of type "receiver-auditor-url-acceptable". - balanceReceiverAuditorUrlAcceptable: AmountJson; - - // Balance of type "receiver-depositable". - balanceReceiverDepositable: AmountJson; - - // Balance that is depositable with the exchange, reduced by the - // exchange's debit restrictions and wire fee configuration. - balanceExchangeDepositable: AmountJson; - - // Estimated maximum amount that the wallet could pay for, - // under the assumption that the merchant pays absolutely no fees. - maxMerchantEffectiveDepositAmount: AmountJson; - } diff --git a/core/wallet-core/balances/get-balance-detail.rst b/core/wallet-core/balances/get-balance-detail.rst @@ -0,0 +1,73 @@ +.. ts:op:: getBalanceDetail + :read-only: + + Get detailed balance information for one currency. + + Unlike :ts:op:`getBalances`, which reports one balance per scope, + this operation aggregates the wallet's funds in the given currency + across all exchanges known to the wallet and reports how much of the + balance is spendable under progressively stricter criteria. + + **Request:** + + The request ``args`` must be a `GetBalanceDetailRequest` object. + + **Response:** + + On success, the result is a `PaymentBalanceDetails` object. + + **Details:** + + ``balanceAvailable`` covers funds available for spending, including + amounts expected from pending refreshes. ``balanceMaterial`` is the + balance the wallet believes it could spend right now, without waiting + for any operations to complete. The remaining balances are subsets + of the material balance: ``balanceAgeAcceptable`` applies an age + restriction, the ``balanceReceiver*Acceptable`` balances restrict to + funds that a receiver accepts based on exchange URL, exchange public + key or auditor URL, and the depositable balances additionally require + that the funds can be deposited via a supported wire method. + +.. ts:def:: GetBalanceDetailRequest + + interface GetBalanceDetailRequest { + // Currency to compute the balance details for. + currency: string; + } + +.. ts:def:: PaymentBalanceDetails + + interface PaymentBalanceDetails { + // Balance of type "available" (see details above). + balanceAvailable: AmountJson; + + // Balance of type "material" (see details above). + balanceMaterial: AmountJson; + + // Balance of type "age-acceptable" (see details above). + balanceAgeAcceptable: AmountJson; + + // Balance of type "receiver-acceptable" (see details above). + // Deprecated, use the balanceReceiver*Acceptable balances instead. + balanceReceiverAcceptable: AmountJson; + + // Balance of type "receiver-exchange-url-acceptable". + balanceReceiverExchangeUrlAcceptable: AmountJson; + + // Balance of type "receiver-exchange-pub-acceptable". + balanceReceiverExchangePubAcceptable: AmountJson; + + // Balance of type "receiver-auditor-url-acceptable". + balanceReceiverAuditorUrlAcceptable: AmountJson; + + // Balance of type "receiver-depositable". + balanceReceiverDepositable: AmountJson; + + // Balance that is depositable with the exchange, reduced by the + // exchange's debit restrictions and wire fee configuration. + balanceExchangeDepositable: AmountJson; + + // Estimated maximum amount that the wallet could pay for, + // under the assumption that the merchant pays absolutely no fees. + maxMerchantEffectiveDepositAmount: AmountJson; + } diff --git a/core/wallet-core/balances/get-balances.rst b/core/wallet-core/balances/get-balances.rst @@ -0,0 +1,216 @@ +.. ts:op:: getBalances + :read-only: + + Get current wallet balance. + + Balances are reported per *scope* (`ScopeInfo`): funds held globally + for a currency, at a particular exchange, under a particular auditor + or under a superseded exchange master key are reported as separate + balances. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `BalancesResponse` object. + + **Details:** + + Each balance distinguishes three amounts. ``available`` is the + balance available for spending from transactions in their final state, + plus amounts expected to become available from pending refreshes. + ``pendingIncoming`` is the expected positive delta to the available + balance once pending operations (such as withdrawals or incoming peer + payments) reach the "done" state. ``pendingOutgoing`` is the amount + currently allocated to spend operations that could still be aborted, + in which case part of the amount may be recovered. + +.. ts:def:: BalancesResponse + + interface BalancesResponse { + // Electronic cash balances, per currency scope. + balances: WalletBalance[]; + + // Does the user have money from an exchange other than demo or test? + haveProdBalance: boolean; + + // Summary of donations, per donau/year/currency. + donauSummary?: DonauSummaryItem[]; + } + +.. ts:def:: WalletBalance + + interface WalletBalance { + // DD71 expiry information and conditional + // cost of keeping this balance. + refreshInfo?: WalletRefreshInfo; + + // Scope of the funds covered by this balance. + scopeInfo: ScopeInfo; + + // Balance available for spending, including amounts + // expected from pending refreshes. + available: AmountString; + + // Expected positive delta to the available balance + // from pending operations. + pendingIncoming: AmountString; + + // Amount allocated to spend operations that could still be aborted. + pendingOutgoing: AmountString; + + // Pending KYC or confirmation steps affecting this balance. + flags: BalanceFlag[]; + + // Available URLs for pages that list + // where money in this scope can be spent. + shoppingUrls?: string[]; + + // Are p2p payments disabled for this scope? + disablePeerPayments?: boolean; + + // Are wallet deposits disabled for this scope? + disableDirectDeposits?: boolean; + } + +.. ts:def:: WalletRefreshInfo + + interface WalletRefreshInfo { + risks: CashExpirationRisk[]; + recoveries: CashRenewalNotice[]; + annualCostBound: AnnualRefreshCostBound; + } + +.. ts:def:: CashExpirationRisk + + interface CashExpirationRisk { + exchangeBaseUrl: string; + exchangeMasterPub: string; + amount: AmountString; + earliestDepositExpiration: TalerProtocolTimestamp; + reason: + | "pending" + | "connectivity" + | "exchange-error" + | "no-replacement" + | "checking" + | "invalid-lifetime"; + } + +.. ts:def:: CashRenewalNotice + + interface CashRenewalNotice { + warningId: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; + amount: AmountString; + oldDepositExpiration: TalerProtocolTimestamp; + newDepositExpiration: TalerProtocolTimestamp; + // Earliest emergency threshold of the renewed coins. + nextRelevantDate: TalerProtocolTimestamp; + } + +.. ts:def:: AnnualRefreshCostBound + + // Conditional on stable, continuously available compatible + // offerings and timely reveal. + type AnnualRefreshCostBound = { + horizonDays: 365; + projection: "stable-current-offerings"; + } & ( + | { + status: "available"; + amount: AmountString; + } + | { + status: "unavailable"; + reasons: string[]; + } + ); + +.. ts:def:: ScopeInfo + + // Scope of a balance; identifies the trust domain + // the funds belong to. + type ScopeInfo = + | ScopeInfoGlobal + | ScopeInfoExchange + | ScopeInfoAuditor + | ScopeInfoExchangeLegacyKeys; + +.. ts:def:: ScopeInfoGlobal + + // Funds held with an exchange that is globally + // trusted for the currency. + type ScopeInfoGlobal = { + type: "global"; + currency: string; + }; + +.. ts:def:: ScopeInfoExchange + + // Funds held at one particular exchange. + type ScopeInfoExchange = { + type: "exchange"; + currency: string; + url: string; + }; + +.. ts:def:: ScopeInfoAuditor + + // Funds whose denominations are audited by a globally + // trusted auditor. + type ScopeInfoAuditor = { + type: "auditor"; + currency: string; + url: string; + }; + +.. ts:def:: ScopeInfoExchangeLegacyKeys + + // Funds issued under a master public key that the exchange has + // since replaced; never pooled with funds under the current key. + type ScopeInfoExchangeLegacyKeys = { + type: "exchange-legacy-keys"; + currency: string; + url: string; + // The superseded key the funds were issued under. + masterPub: string; + }; + +.. ts:def:: BalanceFlag + + // Flag marking a pending KYC, AML or confirmation step for + // the incoming or outgoing funds of a balance. + type BalanceFlag = + | "incoming-kyc" + | "incoming-aml" + | "incoming-confirmation" + | "outgoing-kyc"; + +.. ts:def:: DonauSummaryItem + + interface DonauSummaryItem { + // Base URL of the donau service. + donauBaseUrl: string; + + // Legal domain of the donau service (if available). + legalDomain?: string; + + // Year of the donation(s). + year: number; + + // Sum of donation receipts received from merchants + // in the applicable year. + amountReceiptsAvailable: AmountString; + + // Sum of donation receipts already submitted to the + // donau in the applicable year. + amountReceiptsSubmitted: AmountString; + + // Amount of the latest available statement. Missing + // if no statement was requested yet. + amountStatement?: AmountString; + } diff --git a/core/wallet-core/bank-accounts.rst b/core/wallet-core/bank-accounts.rst @@ -1,162 +0,0 @@ -.. _wallet-op-listBankAccounts: - -**listBankAccounts** - -List bank accounts known to the wallet from previous withdrawals. - -**Request:** - - The request must be a `ListBankAccountsRequest` object. - -**Response:** - - On success, the result is a `ListBankAccountsResponse` object. - -**Details:** - - When ``currency`` is specified, only accounts that support the - currency are returned; accounts whose supported currencies are - unknown are always included. - -.. ts:def:: ListBankAccountsRequest - - interface ListBankAccountsRequest { - currency?: string; - } - -.. ts:def:: ListBankAccountsResponse - - interface ListBankAccountsResponse { - accounts: WalletBankAccountInfo[]; - } - -.. ts:def:: WalletBankAccountInfo - - interface WalletBankAccountInfo { - bankAccountId: string; - - paytoUri: string; - - // Did we previously complete a KYC process for this bank account? - // Deprecated: the KYC may have been completed for one exchange - // but not for another. - kycCompleted: boolean; - - // Currencies supported by the bank, if known. - currencies: string[] | undefined; - - label: string | undefined; - } - - -.. _wallet-op-getBankAccountById: - -**getBankAccountById** - -Get a known bank account by its identifier. - -**Request:** - - The request must be a `GetBankAccountByIdRequest` object. - -**Response:** - - On success, the result is a `GetBankAccountByIdResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_BANK_ACCOUNT_NOT_FOUND``. - -.. ts:def:: GetBankAccountByIdRequest - - interface GetBankAccountByIdRequest { - bankAccountId: string; - } - -.. ts:def:: GetBankAccountByIdResponse - - type GetBankAccountByIdResponse = WalletBankAccountInfo; - - -.. _wallet-op-addBankAccount: - -**addBankAccount** - -Add a bank account to the wallet's list of known bank accounts. - -**Request:** - - The request must be an `AddBankAccountRequest` object. - -**Response:** - - On success, the result is an `AddBankAccountResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``GENERIC_PAYTO_URI_MALFORMED``, ``WALLET_BANK_ACCOUNT_NOT_FOUND``. - -**Details:** - - If an account with the same ``paytoUri`` is already known, it is - updated (the supported currencies are merged) and its identifier - is returned. When ``replaceBankAccountId`` is specified, the - account with that identifier is replaced and keeps its identifier; - the request fails with ``WALLET_BANK_ACCOUNT_NOT_FOUND`` when no - such account exists. A ``bank-account-change`` notification is - emitted. - -.. ts:def:: AddBankAccountRequest - - interface AddBankAccountRequest { - // Payto URI of the bank account that should be added. - paytoUri: string; - - // Human-readable label for the account. - label: string; - - // Currencies supported by the bank (if known). - currencies?: string[] | undefined; - - // Bank account that this new account should replace. - replaceBankAccountId?: string; - } - -.. ts:def:: AddBankAccountResponse - - interface AddBankAccountResponse { - // Identifier of the added bank account. - bankAccountId: string; - } - - -.. _wallet-op-forgetBankAccount: - -**forgetBankAccount** - -Remove a known bank account. - -**Request:** - - The request must be a `ForgetBankAccountRequest` object. - -**Response:** - - On success, the result is an empty object (`EmptyObject`). - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_BANK_ACCOUNT_NOT_FOUND``. - -**Details:** - - A ``bank-account-change`` notification is emitted. - -.. ts:def:: ForgetBankAccountRequest - - interface ForgetBankAccountRequest { - bankAccountId: string; - } diff --git a/core/wallet-core/bank-accounts/add-bank-account.rst b/core/wallet-core/bank-accounts/add-bank-account.rst @@ -0,0 +1,54 @@ +.. ts:op:: addBankAccount + + Add a bank account to the wallet's list of known bank accounts. + + **Request:** + + The request must be an `AddBankAccountRequest` object. + + **Response:** + + On success, the result is an `AddBankAccountResponse` object. + + **Side effects:** + + Adds the bank account to the wallet's database, or updates the + existing account with the same ``paytoUri``. A + ``bank-account-change`` notification is emitted. + + **Expected errors:** + + The caller can handle the following errors inline: + ``GENERIC_PAYTO_URI_MALFORMED``, ``WALLET_BANK_ACCOUNT_NOT_FOUND``. + + **Details:** + + If an account with the same ``paytoUri`` is already known, it is + updated (the supported currencies are merged) and its identifier + is returned. When ``replaceBankAccountId`` is specified, the + account with that identifier is replaced and keeps its + identifier; the request fails with + ``WALLET_BANK_ACCOUNT_NOT_FOUND`` when no such account exists. + +.. ts:def:: AddBankAccountRequest + + interface AddBankAccountRequest { + // Payto URI of the bank account that should be added. + paytoUri: string; + + // Human-readable label for the account. + label: string; + + // Currencies supported by the bank (if known). + currencies?: string[] | undefined; + + // Bank account that this new account should replace. + replaceBankAccountId?: string; + } + +.. ts:def:: AddBankAccountResponse + + interface AddBankAccountResponse { + // Identifier of the added bank account. + bankAccountId: string; + } diff --git a/core/wallet-core/bank-accounts/forget-bank-account.rst b/core/wallet-core/bank-accounts/forget-bank-account.rst @@ -0,0 +1,27 @@ +.. ts:op:: forgetBankAccount + + Remove a known bank account. + + **Request:** + + The request must be a `ForgetBankAccountRequest` object. + + **Response:** + + On success, the result is an empty object (`EmptyObject`). + + **Side effects:** + + Removes the bank account from the wallet's database. A + ``bank-account-change`` notification is emitted. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_BANK_ACCOUNT_NOT_FOUND``. + +.. ts:def:: ForgetBankAccountRequest + + interface ForgetBankAccountRequest { + bankAccountId: string; + } diff --git a/core/wallet-core/bank-accounts/get-bank-account-by-id.rst b/core/wallet-core/bank-accounts/get-bank-account-by-id.rst @@ -0,0 +1,27 @@ +.. ts:op:: getBankAccountById + :read-only: + + Get a known bank account by its identifier. + + **Request:** + + The request must be a `GetBankAccountByIdRequest` object. + + **Response:** + + On success, the result is a `GetBankAccountByIdResponse` object. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_BANK_ACCOUNT_NOT_FOUND``. + +.. ts:def:: GetBankAccountByIdRequest + + interface GetBankAccountByIdRequest { + bankAccountId: string; + } + +.. ts:def:: GetBankAccountByIdResponse + + type GetBankAccountByIdResponse = WalletBankAccountInfo; diff --git a/core/wallet-core/bank-accounts/list-bank-accounts.rst b/core/wallet-core/bank-accounts/list-bank-accounts.rst @@ -0,0 +1,48 @@ +.. ts:op:: listBankAccounts + :read-only: + + List bank accounts known to the wallet from previous withdrawals. + + **Request:** + + The request must be a `ListBankAccountsRequest` object. + + **Response:** + + On success, the result is a `ListBankAccountsResponse` object. + + **Details:** + + When ``currency`` is specified, only accounts that support the + currency are returned; accounts whose supported currencies are + unknown are always included. + +.. ts:def:: ListBankAccountsRequest + + interface ListBankAccountsRequest { + currency?: string; + } + +.. ts:def:: ListBankAccountsResponse + + interface ListBankAccountsResponse { + accounts: WalletBankAccountInfo[]; + } + +.. ts:def:: WalletBankAccountInfo + + interface WalletBankAccountInfo { + bankAccountId: string; + + paytoUri: string; + + // Did we previously complete a KYC process for this bank account? + // Deprecated: the KYC may have been completed for one exchange + // but not for another. + kycCompleted: boolean; + + // Currencies supported by the bank, if known. + currencies: string[] | undefined; + + label: string | undefined; + } diff --git a/core/wallet-core/contacts.rst b/core/wallet-core/contacts.rst @@ -1,96 +0,0 @@ -.. _wallet-op-addContact: - -**addContact** - -Add a contact to the wallet's contact list. Contacts are identified by -the pair of ``alias`` and ``aliasType``; if a contact with the same -alias and alias type already exists, it is updated. After the contact -list has changed, wallet-core emits a ``contact-added`` notification. - -**Request:** - - The request arguments are an `AddContactRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: AddContactRequest - - interface AddContactRequest { - // The contact to add or update. - contact: ContactEntry; - } - -.. ts:def:: ContactEntry - - interface ContactEntry { - // Contact alias. - alias: string; - - // Alias type. - aliasType: string; - - // Mailbox URI. - mailboxBaseUri: string; - - // Mailbox identity. - mailboxAddress: HashCodeString; - - // The source of this contact, may be a URI. - source: string; - - // The local petname of the contact. - petname: string; - } - - -.. _wallet-op-deleteContact: - -**deleteContact** - -Delete a contact from the wallet's contact list. After the contact -list has changed, wallet-core emits a ``contact-deleted`` notification. - -**Request:** - - The request arguments are a `DeleteContactRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - Only the ``alias`` and ``aliasType`` fields of the given contact are - used to identify the contact to delete; the remaining fields are - ignored. Deleting a contact that does not exist is not an error. - -.. ts:def:: DeleteContactRequest - - interface DeleteContactRequest { - // The contact to delete. - contact: ContactEntry; - } - - -.. _wallet-op-getContacts: - -**getContacts** - -Get all contacts from the wallet's contact list. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `ContactListResponse` object. - -.. ts:def:: ContactListResponse - - interface ContactListResponse { - // All contacts stored in the wallet. - contacts: ContactEntry[]; - } diff --git a/core/wallet-core/contacts/add-contact.rst b/core/wallet-core/contacts/add-contact.rst @@ -0,0 +1,48 @@ +.. ts:op:: addContact + + Add a contact to the wallet's contact list. Contacts are identified + by the pair of ``alias`` and ``aliasType``; if a contact with the + same alias and alias type already exists, it is updated. + + **Request:** + + The request arguments are an `AddContactRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Adds or updates the contact in the wallet's contact list. After + the contact list has changed, wallet-core emits a + ``contact-added`` notification. + +.. ts:def:: AddContactRequest + + interface AddContactRequest { + // The contact to add or update. + contact: ContactEntry; + } + +.. ts:def:: ContactEntry + + interface ContactEntry { + // Contact alias. + alias: string; + + // Alias type. + aliasType: string; + + // Mailbox URI. + mailboxBaseUri: string; + + // Mailbox identity. + mailboxAddress: HashCodeString; + + // The source of this contact, may be a URI. + source: string; + + // The local petname of the contact. + petname: string; + } diff --git a/core/wallet-core/contacts/delete-contact.rst b/core/wallet-core/contacts/delete-contact.rst @@ -0,0 +1,30 @@ +.. ts:op:: deleteContact + + Delete a contact from the wallet's contact list. + + **Request:** + + The request arguments are a `DeleteContactRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes the contact from the wallet's contact list and emits a + ``contact-deleted`` notification. + + **Details:** + + Only the ``alias`` and ``aliasType`` fields of the given contact + are used to identify the contact to delete; the remaining fields + are ignored. Deleting a contact that does not exist is not an + error. + +.. ts:def:: DeleteContactRequest + + interface DeleteContactRequest { + // The contact to delete. + contact: ContactEntry; + } diff --git a/core/wallet-core/contacts/get-contacts.rst b/core/wallet-core/contacts/get-contacts.rst @@ -0,0 +1,19 @@ +.. ts:op:: getContacts + :read-only: + + Get all contacts from the wallet's contact list. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `ContactListResponse` object. + +.. ts:def:: ContactListResponse + + interface ContactListResponse { + // All contacts stored in the wallet. + contacts: ContactEntry[]; + } diff --git a/core/wallet-core/database.rst b/core/wallet-core/database.rst @@ -1,239 +0,0 @@ -.. _wallet-op-importDb: - -**importDb** - -Import a wallet database dump, replacing the current database -contents. - -**Request:** - - The request arguments must be an `ImportDbRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_DB_BACKEND_UNSUPPORTED``, ``WALLET_CORE_API_BAD_REQUEST``, - ``WALLET_CORE_REQUEST_CANCELLED``. - -**Details:** - - The format of ``dump`` (JSON or SQLite) is detected automatically. - A ``dump`` that does not look like a valid wallet database dump is - rejected with ``WALLET_CORE_API_BAD_REQUEST``; if the active - database backend cannot import the dump's format, the request fails - with ``WALLET_DB_BACKEND_UNSUPPORTED``. When ``progressToken`` is - set, progress is reported via notifications and the import can be - cancelled with - :ref:`cancelProgressToken <wallet-op-cancelProgressToken>`; a - cancelled import fails with ``WALLET_CORE_REQUEST_CANCELLED``. - -.. ts:def:: ImportDbRequest - - interface ImportDbRequest { - dump?: any; - - // Correlates progress notifications and allows cancellation. - progressToken?: string; - } - - -.. _wallet-op-exportDb: - -**exportDb** - -Export the wallet database's contents to JSON. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an unspecified JSON value. - - -.. _wallet-op-exportDbToFile: - -**exportDbToFile** - -Export the database to a file. The target directory must already -exist. - -**Request:** - - The request arguments must be an `ExportDbToFileRequest` object. - -**Response:** - - On success, the result is an `ExportDbToFileResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_DB_BACKEND_UNSUPPORTED``. - -**Details:** - - The request fails with ``WALLET_DB_BACKEND_UNSUPPORTED`` if the - active database backend cannot export the database to a file. - -.. ts:def:: ExportDbToFileRequest - - interface ExportDbToFileRequest { - // Directory that the DB should be exported into. - directory: string; - - // Stem of the exported DB filename. The final name will be - // ${directory}/${stem}.${extension}, where the extension depends - // on the used DB backend. - stem: string; - - // Force the format of the export. Supported values on - // filesystem-capable hosts are "json" and "sqlite3"; if omitted, - // the host's default is "sqlite3". - forceFormat?: string; - } - -.. ts:def:: ExportDbToFileResponse - - interface ExportDbToFileResponse { - // Full path to the backup. - path: string; - } - - -.. _wallet-op-importDbFromFile: - -**importDbFromFile** - -Import the database from a JSON or SQLite file. **Caution:** this -overrides existing data. - -**Request:** - - The request arguments must be an `ImportDbFromFileRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_DB_BACKEND_UNSUPPORTED``, ``WALLET_CORE_API_BAD_REQUEST``, - ``WALLET_CORE_REQUEST_CANCELLED``. - -**Details:** - - The ``path`` must end in ``.json`` or ``.sqlite3``; other files are - rejected with ``WALLET_CORE_API_BAD_REQUEST``, as is a file that - cannot be read. The ``progressToken`` behaves as for - :ref:`importDb <wallet-op-importDb>`. - -.. ts:def:: ImportDbFromFileRequest - - interface ImportDbFromFileRequest { - // Full path to a .json or .sqlite3 backup. - path: string; - - // Correlates progress notifications and allows cancellation. - progressToken?: string; - } - - -.. _wallet-op-migrateDatabase: - -**migrateDatabase** - -Explicitly migrate an IndexedDB-emulation wallet to the native SQLite -schema. The operation is idempotent when the wallet is already -native. - -**Request:** - - The request arguments must be a `MigrateDatabaseRequest` object. - -**Response:** - - On success, the result is a `MigrateDatabaseResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_DB_BACKEND_UNSUPPORTED``, ``WALLET_DB_UNAVAILABLE``, - ``WALLET_CORE_REQUEST_CANCELLED``. - -**Details:** - - When the wallet already uses the native SQLite schema, the request - succeeds with ``migrated`` set to false. The optional - ``progressToken`` correlates progress notifications and allows - cancellation via - :ref:`cancelProgressToken <wallet-op-cancelProgressToken>`. - -.. ts:def:: MigrateDatabaseRequest - - interface MigrateDatabaseRequest { - // Enables progress correlation and cancellation through - // cancelProgressToken. - progressToken?: string; - } - -.. ts:def:: MigrateDatabaseResponse - - interface MigrateDatabaseResponse { - // Whether this request changed the active database backend. - migrated: boolean; - - // Database backend active after the request. - databaseBackend: WalletDatabaseBackend; - } - -.. ts:def:: WalletDatabaseBackend - - type WalletDatabaseBackend = "indexeddb" | "sqlite"; - - -.. _wallet-op-clearDb: - -**clearDb** - -Dangerously clear the whole wallet database. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - -**Details:** - - In addition to the database contents, all in-memory caches and the - recorded flight records are cleared, and the background task - scheduler is reloaded. - - -.. _wallet-op-recycle: - -**recycle** - -Export a backup, clear the database and re-import it. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - -**Details:** - - This operation is declared but not implemented yet: every request - currently fails with ``GENERIC_FEATURE_NOT_IMPLEMENTED``. diff --git a/core/wallet-core/database/clear-db.rst b/core/wallet-core/database/clear-db.rst @@ -0,0 +1,21 @@ +.. ts:op:: clearDb + + Dangerously clear the whole wallet database. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Irreversibly deletes the entire wallet database. + + **Details:** + + In addition to the database contents, all in-memory caches and the + recorded flight records are cleared, and the background task + scheduler is reloaded. diff --git a/core/wallet-core/database/export-db-to-file.rst b/core/wallet-core/database/export-db-to-file.rst @@ -0,0 +1,50 @@ +.. ts:op:: exportDbToFile + + Export the database to a file. The target directory must already + exist. + + **Request:** + + The request arguments must be an `ExportDbToFileRequest` object. + + **Response:** + + On success, the result is an `ExportDbToFileResponse` object. + + **Side effects:** + + Writes the database dump to a new file on the host filesystem. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_DB_BACKEND_UNSUPPORTED``. + + **Details:** + + The request fails with ``WALLET_DB_BACKEND_UNSUPPORTED`` if the + active database backend cannot export the database to a file. + +.. ts:def:: ExportDbToFileRequest + + interface ExportDbToFileRequest { + // Directory that the DB should be exported into. + directory: string; + + // Stem of the exported DB filename. The final name will be + // ${directory}/${stem}.${extension}, where the extension depends + // on the used DB backend. + stem: string; + + // Force the format of the export. Supported values on + // filesystem-capable hosts are "json" and "sqlite3"; if omitted, + // the host's default is "sqlite3". + forceFormat?: string; + } + +.. ts:def:: ExportDbToFileResponse + + interface ExportDbToFileResponse { + // Full path to the backup. + path: string; + } diff --git a/core/wallet-core/database/export-db.rst b/core/wallet-core/database/export-db.rst @@ -0,0 +1,12 @@ +.. ts:op:: exportDb + :read-only: + + Export the wallet database's contents to JSON. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an unspecified JSON value. diff --git a/core/wallet-core/database/import-db-from-file.rst b/core/wallet-core/database/import-db-from-file.rst @@ -0,0 +1,40 @@ +.. ts:op:: importDbFromFile + + Import the database from a JSON or SQLite file. **Caution:** this + overrides existing data. + + **Request:** + + The request arguments must be an `ImportDbFromFileRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Replaces the entire contents of the wallet database with the dump + read from the given file. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_DB_BACKEND_UNSUPPORTED``, ``WALLET_CORE_API_BAD_REQUEST``, + ``WALLET_CORE_REQUEST_CANCELLED``. + + **Details:** + + The ``path`` must end in ``.json`` or ``.sqlite3``; other files + are rejected with ``WALLET_CORE_API_BAD_REQUEST``, as is a file + that cannot be read. The ``progressToken`` behaves as for + :ts:op:`importDb`. + +.. ts:def:: ImportDbFromFileRequest + + interface ImportDbFromFileRequest { + // Full path to a .json or .sqlite3 backup. + path: string; + + // Correlates progress notifications and allows cancellation. + progressToken?: string; + } diff --git a/core/wallet-core/database/import-db.rst b/core/wallet-core/database/import-db.rst @@ -0,0 +1,43 @@ +.. ts:op:: importDb + + Import a wallet database dump, replacing the current database + contents. + + **Request:** + + The request arguments must be an `ImportDbRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Replaces the entire contents of the wallet database with the + imported dump. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_DB_BACKEND_UNSUPPORTED``, ``WALLET_CORE_API_BAD_REQUEST``, + ``WALLET_CORE_REQUEST_CANCELLED``. + + **Details:** + + The format of ``dump`` (JSON or SQLite) is detected automatically. + A ``dump`` that does not look like a valid wallet database dump is + rejected with ``WALLET_CORE_API_BAD_REQUEST``; if the active + database backend cannot import the dump's format, the request + fails with ``WALLET_DB_BACKEND_UNSUPPORTED``. When + ``progressToken`` is set, progress is reported via notifications + and the import can be cancelled with :ts:op:`cancelProgressToken`; + a cancelled import fails with ``WALLET_CORE_REQUEST_CANCELLED``. + +.. ts:def:: ImportDbRequest + + interface ImportDbRequest { + dump?: any; + + // Correlates progress notifications and allows cancellation. + progressToken?: string; + } diff --git a/core/wallet-core/database/migrate-database.rst b/core/wallet-core/database/migrate-database.rst @@ -0,0 +1,54 @@ +.. ts:op:: migrateDatabase + + Explicitly migrate an IndexedDB-emulation wallet to the native + SQLite schema. The operation is idempotent when the wallet is + already native. + + **Request:** + + The request arguments must be a `MigrateDatabaseRequest` object. + + **Response:** + + On success, the result is a `MigrateDatabaseResponse` object. + + **Side effects:** + + Converts the wallet database to the native SQLite schema. The + migration can be long-running; progress is reported via + notifications when ``progressToken`` is set. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_DB_BACKEND_UNSUPPORTED``, ``WALLET_DB_UNAVAILABLE``, + ``WALLET_CORE_REQUEST_CANCELLED``. + + **Details:** + + When the wallet already uses the native SQLite schema, the request + succeeds with ``migrated`` set to false. The optional + ``progressToken`` correlates progress notifications and allows + cancellation via :ts:op:`cancelProgressToken`. + +.. ts:def:: MigrateDatabaseRequest + + interface MigrateDatabaseRequest { + // Enables progress correlation and cancellation through + // cancelProgressToken. + progressToken?: string; + } + +.. ts:def:: MigrateDatabaseResponse + + interface MigrateDatabaseResponse { + // Whether this request changed the active database backend. + migrated: boolean; + + // Database backend active after the request. + databaseBackend: WalletDatabaseBackend; + } + +.. ts:def:: WalletDatabaseBackend + + type WalletDatabaseBackend = "indexeddb" | "sqlite"; diff --git a/core/wallet-core/database/recycle.rst b/core/wallet-core/database/recycle.rst @@ -0,0 +1,19 @@ +.. ts:op:: recycle + + Export a backup, clear the database and re-import it. + + This operation is declared but **not implemented yet**: every + request currently fails with ``GENERIC_FEATURE_NOT_IMPLEMENTED``. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + None. The operation is declared but not implemented yet and + always fails with ``GENERIC_FEATURE_NOT_IMPLEMENTED``. diff --git a/core/wallet-core/deposits.rst b/core/wallet-core/deposits.rst @@ -1,421 +0,0 @@ -.. _wallet-op-checkDeposit: - -**checkDeposit** - -Check whether a deposit of the given amount to the given target account -is possible with the funds in the wallet, and calculate the associated -fees, without creating a deposit transaction. - -**Request:** - - The request body must be a `CheckDepositRequest` object. - -**Response:** - - On success, the result is a `CheckDepositResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``GENERIC_PAYTO_URI_MALFORMED``, ``WALLET_NO_SUITABLE_EXCHANGE``, - ``WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE``. - -**Details:** - - ``totalDepositCost`` is the total cost to the wallet: the - contributions of the selected coins plus the cost of refreshing any - change. ``effectiveDepositAmount`` is the amount expected to be - wired to the destination account (not considering aggregation). - -.. ts:def:: CheckDepositRequest - - interface CheckDepositRequest { - // Payto URI to identify the (bank) account that the exchange will - // wire the money to. - depositPaytoUri: string; - - // Instructed amount used for deposit coin selection. The response - // reports the resulting wallet cost and destination amount, which - // can differ because of fees and refresh change. - amount: AmountString; - - // Restrict the deposit to a certain scope. - restrictScope?: ScopeInfo; - - progressToken?: string; - } - -.. ts:def:: CheckDepositResponse - - interface CheckDepositResponse { - totalDepositCost: AmountString; - effectiveDepositAmount: AmountString; - fees: DepositGroupFees; - - kycSoftLimit?: AmountString; - kycHardLimit?: AmountString; - - // Base URL of exchanges that would likely require soft KYC. - kycExchanges?: string[]; - } - -.. ts:def:: DepositGroupFees - - interface DepositGroupFees { - // Deposit fees of the selected coins. - coin: AmountString; - - // Wire fees of the involved exchanges. - wire: AmountString; - - // Cost of refreshing change from the selected coins. - refresh: AmountString; - } - - -.. _wallet-op-createDepositGroup: - -**createDepositGroup** - -Create a new deposit group. Deposit groups are used to deposit multiple -coins to a bank account, usually the wallet user's own bank account. - -The deposit is executed asynchronously as a transaction; the response -returns the transaction identifier and its initial state. The fees of -the deposit can be reviewed beforehand with :ref:`checkDeposit -<wallet-op-checkDeposit>`. - -**Request:** - - The request body must be a `CreateDepositGroupRequest` object. - -**Response:** - - On success, the result is a `CreateDepositGroupResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``GENERIC_PAYTO_URI_MALFORMED``, ``WALLET_NO_SUITABLE_EXCHANGE``, - ``WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE``, - ``WALLET_KYC_LIMIT_EXCEEDED``. - -.. ts:def:: CreateDepositGroupRequest - - interface CreateDepositGroupRequest { - depositPaytoUri: string; - - // Instructed amount used for deposit coin selection. - amount: AmountString; - - // Restrict the deposit to a certain scope. - restrictScope?: ScopeInfo; - - // Use a fixed merchant private key. - testingFixedPriv?: string; - - // Optional wire deadline for the deposits. - wireDeadline?: TalerProtocolTimestamp; - - // Pre-allocated transaction ID. Allows clients to easily handle - // notifications that occur while the operation has been created - // but before the creation request has returned. - transactionId?: TransactionIdStr; - - progressToken?: string; - } - -.. ts:def:: CreateDepositGroupResponse - - // Response to a createDepositGroup request. - interface CreateDepositGroupResponse { - // Transaction ID of the newly created deposit transaction. - transactionId: TransactionIdStr; - - // Current state of the new deposit transaction. Returned as a - // performance optimization, so that the UI doesn't have to do a - // separate getTransactionById. - txState: TransactionState; - - // Deprecated, use transactionId instead. - depositGroupId: string; - } - - -.. _wallet-op-convertDepositAmount: - -**convertDepositAmount** - -This operation is **deprecated**. Use :ref:`checkDeposit -<wallet-op-checkDeposit>` for a concrete instructed amount, or -:ref:`getMaxDepositAmount <wallet-op-getMaxDepositAmount>` to query -deposit limits. - -Compute the effective and raw amounts for a deposit of the given -instructed amount, based on the coins currently available in the -wallet. The ``type`` field (`TransactionAmountMode`) selects whether -``amount`` is interpreted as an effective or as a raw amount. - -**Request:** - - The request body must be a `ConvertAmountRequest` object. - -**Response:** - - On success, the result is an `AmountResponse` object. - -.. ts:def:: ConvertAmountRequest - - // Deprecated: use CheckDepositRequest for a concrete instructed - // amount, or GetMaxDepositAmountRequest to query deposit limits. - interface ConvertAmountRequest { - amount: AmountString; - type: TransactionAmountMode; - depositPaytoUri: PaytoString; - } - -.. ts:def:: AmountResponse - - interface AmountResponse { - effectiveAmount: AmountString; - rawAmount: AmountString; - } - - -.. _wallet-op-getMaxDepositAmount: - -**getMaxDepositAmount** - -Query the maximum amount that can currently be deposited in the given -currency. - -**Request:** - - The request body must be a `GetMaxDepositAmountRequest` object. When - ``depositPaytoUri`` is omitted, wire-method eligibility, account - restrictions and wire fees cannot be reflected in the response. - -**Response:** - - On success, the result is a `GetMaxDepositAmountResponse` object. - -**Details:** - - The result distinguishes the maximum that can be deposited - immediately (``material``) from the maximum that also includes the - expected outputs of pending refresh operations (``available``). - ``exchangeDiagnostics`` gives, for every ready same-currency - exchange, the per-exchange maximums and the reasons why the exchange - cannot serve the deposit. - -.. ts:def:: GetMaxDepositAmountRequest - - interface GetMaxDepositAmountRequest { - // Currency to deposit. - currency: string; - - // Target bank account to deposit into. When omitted, wire-method - // eligibility, account restrictions and wire fees cannot be - // reflected in the response. - depositPaytoUri?: string; - - // Restrict the deposit to a certain scope. - restrictScope?: ScopeInfo; - } - -.. ts:def:: GetMaxDepositAmountResponse - - interface GetMaxDepositAmountResponse { - // Maximum that can be deposited immediately. - material: DepositMaximum; - - // Maximum including expected outputs of pending refresh - // operations. - available: DepositMaximum; - - // Eligibility and maximum amounts for every ready same-currency - // exchange. - exchangeDiagnostics: Record<string, DepositExchangeDiagnostics>; - } - -.. ts:def:: DepositMaximum - - // Maximum amounts and fees for one coherent deposit coin selection. - interface DepositMaximum { - // Gross target amount passed to checkDeposit or - // createDepositGroup. - instructedAmount: AmountString; - - // Total balance effect on the wallet: instructed amount plus fees - // paid by the customer and the cost of refreshing any change. - effectiveAmount: AmountString; - - // Amount expected to reach the destination account: instructed - // amount minus fees covered by the counterparty. - rawAmount: AmountString; - - // Total fees incurred by this deposit selection. - fees: DepositGroupFees; - } - -.. ts:def:: DepositExchangeDiagnostics - - interface DepositExchangeDiagnostics { - // Maximum that can be deposited immediately. - material: DepositMaximum; - - // Maximum including expected outputs of pending refresh - // operations. - available: DepositMaximum; - - // Eligibility failures, in deterministic evaluation order. - reasons: DepositEligibilityReason[]; - } - -.. ts:def:: DepositEligibilityReason - - // Reason why a ready, same-currency exchange cannot serve a deposit. - type DepositEligibilityReason = - | { type: "direct-deposit-disabled" } - | { type: "scope-restricted"; scopeInfo: ScopeInfo } - | { type: "wire-method-unsupported"; wireMethod: string } - | { type: "wire-fee-unavailable"; wireMethod: string } - | { - type: "deposit-account-restricted"; - wireMethod: string; - accountRestrictions: Record<string, AccountRestriction[]>; - }; - -.. ts:def:: DepositEligibilityReasonType - - type DepositEligibilityReasonType = - "direct-deposit-disabled" - | "scope-restricted" - | "wire-method-unsupported" - | "wire-fee-unavailable" - | "deposit-account-restricted"; - -.. ts:def:: AccountRestriction - - type AccountRestriction = - | RegexAccountRestriction - | DenyAllAccountRestriction; - -.. ts:def:: RegexAccountRestriction - - // Accounts interacting with this type of account restriction must - // have a payto://-URI matching the given regex. - interface RegexAccountRestriction { - type: "regex"; - - // Regular expression that the payto://-URI of the partner account - // must follow (posix-egrep, without support for character - // classes, GNU extensions, back-references or intervals). - payto_regex: string; - - // Hint for a human to understand the restriction. - human_hint: string; - - // Map from IETF BCP 47 language tags to localized human hints. - human_hint_i18n?: InternationalizedString; - } - -.. ts:def:: DenyAllAccountRestriction - - interface DenyAllAccountRestriction { - type: "deny"; - } - - -.. _wallet-op-getDepositWireTypes: - -**getDepositWireTypes** - -Get the wire types that can be used as the target of a deposit, -together with per-type details. The result is derived from the wire -accounts of the exchanges known to the wallet; accounts with a -deny-type debit restriction are excluded. When ``currency`` is given, -only exchanges for that currency are considered. - -**Request:** - - The request body must be a `GetDepositWireTypesRequest` object. - -**Response:** - - On success, the result is a `GetDepositWireTypesResponse` object. - -.. ts:def:: GetDepositWireTypesRequest - - interface GetDepositWireTypesRequest { - currency?: string; - - // Optional scope info to further restrict the result. - // Currency must match the currency field. - scopeInfo?: ScopeInfo; - } - -.. ts:def:: GetDepositWireTypesResponse - - interface GetDepositWireTypesResponse { - // Details for each wire type. - wireTypeDetails: WireTypeDetails[]; - } - -.. ts:def:: WireTypeDetails - - interface WireTypeDetails { - paymentTargetType: string; - - // Only applicable for payment target type IBAN. Specifies - // whether the user wants to preferably enter their bank account - // details as an IBAN or as a BBAN. Mandatory for - // paymentTargetType="iban". - preferredEntryType?: "iban" | "bban"; - - // Allowed hostnames for the deposit payto URI. Only applicable to - // x-taler-bank. - talerBankHostnames?: string[]; - } - - -.. _wallet-op-getDepositWireTypesForCurrency: - -**getDepositWireTypesForCurrency** - -This operation is **deprecated**. Use :ref:`getDepositWireTypes -<wallet-op-getDepositWireTypes>` instead. - -Get wire types that can be used for a deposit operation with the -provided currency. - -**Request:** - - The request body must be a `GetDepositWireTypesForCurrencyRequest` - object. - -**Response:** - - On success, the result is a `GetDepositWireTypesForCurrencyResponse` - object. - -.. ts:def:: GetDepositWireTypesForCurrencyRequest - - interface GetDepositWireTypesForCurrencyRequest { - currency: string; - - // Optional scope info to further restrict the result. - // Currency must match the currency field. - scopeInfo?: ScopeInfo; - } - -.. ts:def:: GetDepositWireTypesForCurrencyResponse - - // Response with wire types that are supported for a deposit. - interface GetDepositWireTypesForCurrencyResponse { - // Deprecated, use wireTypeDetails instead. - wireTypes: string[]; - - // Details for each wire type. - wireTypeDetails: WireTypeDetails[]; - } diff --git a/core/wallet-core/deposits/check-deposit.rst b/core/wallet-core/deposits/check-deposit.rst @@ -0,0 +1,77 @@ +.. ts:op:: checkDeposit + + Check whether a deposit of the given amount to the given target + account is possible with the funds in the wallet, and calculate the + associated fees, without creating a deposit transaction. + + **Request:** + + The request body must be a `CheckDepositRequest` object. + + **Response:** + + On success, the result is a `CheckDepositResponse` object. + + **Side effects:** + + May refresh the key material of the exchanges holding the selected + coins over the network, including exchange record updates and + notifications. + + **Expected errors:** + + The caller can handle the following errors inline: + ``GENERIC_PAYTO_URI_MALFORMED``, ``WALLET_NO_SUITABLE_EXCHANGE``, + ``WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE``. + + **Details:** + + ``totalDepositCost`` is the total cost to the wallet: the + contributions of the selected coins plus the cost of refreshing any + change. ``effectiveDepositAmount`` is the amount expected to be + wired to the destination account (not considering aggregation). + +.. ts:def:: CheckDepositRequest + + interface CheckDepositRequest { + // Payto URI to identify the (bank) account that the exchange will + // wire the money to. + depositPaytoUri: string; + + // Instructed amount used for deposit coin selection. The response + // reports the resulting wallet cost and destination amount, which + // can differ because of fees and refresh change. + amount: AmountString; + + // Restrict the deposit to a certain scope. + restrictScope?: ScopeInfo; + + progressToken?: string; + } + +.. ts:def:: CheckDepositResponse + + interface CheckDepositResponse { + totalDepositCost: AmountString; + effectiveDepositAmount: AmountString; + fees: DepositGroupFees; + + kycSoftLimit?: AmountString; + kycHardLimit?: AmountString; + + // Base URL of exchanges that would likely require soft KYC. + kycExchanges?: string[]; + } + +.. ts:def:: DepositGroupFees + + interface DepositGroupFees { + // Deposit fees of the selected coins. + coin: AmountString; + + // Wire fees of the involved exchanges. + wire: AmountString; + + // Cost of refreshing change from the selected coins. + refresh: AmountString; + } diff --git a/core/wallet-core/deposits/convert-deposit-amount.rst b/core/wallet-core/deposits/convert-deposit-amount.rst @@ -0,0 +1,37 @@ +.. ts:op:: convertDepositAmount + :read-only: + :deprecated: + + This operation is **deprecated**. Use :ts:op:`checkDeposit` for a + concrete instructed amount, or :ts:op:`getMaxDepositAmount` to query + deposit limits. + + Compute the effective and raw amounts for a deposit of the given + instructed amount, based on the coins currently available in the + wallet. The ``type`` field (`TransactionAmountMode`) selects whether + ``amount`` is interpreted as an effective or as a raw amount. + + **Request:** + + The request body must be a `ConvertAmountRequest` object. + + **Response:** + + On success, the result is an `AmountResponse` object. + +.. ts:def:: ConvertAmountRequest + + // Deprecated: use CheckDepositRequest for a concrete instructed + // amount, or GetMaxDepositAmountRequest to query deposit limits. + interface ConvertAmountRequest { + amount: AmountString; + type: TransactionAmountMode; + depositPaytoUri: PaytoString; + } + +.. ts:def:: AmountResponse + + interface AmountResponse { + effectiveAmount: AmountString; + rawAmount: AmountString; + } diff --git a/core/wallet-core/deposits/create-deposit-group.rst b/core/wallet-core/deposits/create-deposit-group.rst @@ -0,0 +1,71 @@ +.. ts:op:: createDepositGroup + + Create a new deposit group. Deposit groups are used to deposit + multiple coins to a bank account, usually the wallet user's own bank + account. + + The deposit is executed asynchronously as a transaction; the response + returns the transaction identifier and its initial state. The fees + of the deposit can be reviewed beforehand with + :ts:op:`checkDeposit`. + + **Request:** + + The request body must be a `CreateDepositGroupRequest` object. + + **Response:** + + On success, the result is a `CreateDepositGroupResponse` object. + + **Side effects:** + + Creates a deposit group transaction and deposits the coins at the + exchange over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``GENERIC_PAYTO_URI_MALFORMED``, ``WALLET_NO_SUITABLE_EXCHANGE``, + ``WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE``, + ``WALLET_KYC_LIMIT_EXCEEDED``. + +.. ts:def:: CreateDepositGroupRequest + + interface CreateDepositGroupRequest { + depositPaytoUri: string; + + // Instructed amount used for deposit coin selection. + amount: AmountString; + + // Restrict the deposit to a certain scope. + restrictScope?: ScopeInfo; + + // Use a fixed merchant private key. + testingFixedPriv?: string; + + // Optional wire deadline for the deposits. + wireDeadline?: TalerProtocolTimestamp; + + // Pre-allocated transaction ID. Allows clients to easily handle + // notifications that occur while the operation has been created + // but before the creation request has returned. + transactionId?: TransactionIdStr; + + progressToken?: string; + } + +.. ts:def:: CreateDepositGroupResponse + + // Response to a createDepositGroup request. + interface CreateDepositGroupResponse { + // Transaction ID of the newly created deposit transaction. + transactionId: TransactionIdStr; + + // Current state of the new deposit transaction. Returned as a + // performance optimization, so that the UI doesn't have to do a + // separate getTransactionById. + txState: TransactionState; + + // Deprecated, use transactionId instead. + depositGroupId: string; + } diff --git a/core/wallet-core/deposits/get-deposit-wire-types-for-currency.rst b/core/wallet-core/deposits/get-deposit-wire-types-for-currency.rst @@ -0,0 +1,40 @@ +.. ts:op:: getDepositWireTypesForCurrency + :read-only: + :deprecated: + + This operation is **deprecated**. Use + :ts:op:`getDepositWireTypes` instead. + + Get wire types that can be used for a deposit operation with the + provided currency. + + **Request:** + + The request body must be a `GetDepositWireTypesForCurrencyRequest` + object. + + **Response:** + + On success, the result is a `GetDepositWireTypesForCurrencyResponse` + object. + +.. ts:def:: GetDepositWireTypesForCurrencyRequest + + interface GetDepositWireTypesForCurrencyRequest { + currency: string; + + // Optional scope info to further restrict the result. + // Currency must match the currency field. + scopeInfo?: ScopeInfo; + } + +.. ts:def:: GetDepositWireTypesForCurrencyResponse + + // Response with wire types that are supported for a deposit. + interface GetDepositWireTypesForCurrencyResponse { + // Deprecated, use wireTypeDetails instead. + wireTypes: string[]; + + // Details for each wire type. + wireTypeDetails: WireTypeDetails[]; + } diff --git a/core/wallet-core/deposits/get-deposit-wire-types.rst b/core/wallet-core/deposits/get-deposit-wire-types.rst @@ -0,0 +1,49 @@ +.. ts:op:: getDepositWireTypes + :read-only: + + Get the wire types that can be used as the target of a deposit, + together with per-type details. The result is derived from the wire + accounts of the exchanges known to the wallet; accounts with a + deny-type debit restriction are excluded. When ``currency`` is + given, only exchanges for that currency are considered. + + **Request:** + + The request body must be a `GetDepositWireTypesRequest` object. + + **Response:** + + On success, the result is a `GetDepositWireTypesResponse` object. + +.. ts:def:: GetDepositWireTypesRequest + + interface GetDepositWireTypesRequest { + currency?: string; + + // Optional scope info to further restrict the result. + // Currency must match the currency field. + scopeInfo?: ScopeInfo; + } + +.. ts:def:: GetDepositWireTypesResponse + + interface GetDepositWireTypesResponse { + // Details for each wire type. + wireTypeDetails: WireTypeDetails[]; + } + +.. ts:def:: WireTypeDetails + + interface WireTypeDetails { + paymentTargetType: string; + + // Only applicable for payment target type IBAN. Specifies + // whether the user wants to preferably enter their bank account + // details as an IBAN or as a BBAN. Mandatory for + // paymentTargetType="iban". + preferredEntryType?: "iban" | "bban"; + + // Allowed hostnames for the deposit payto URI. Only applicable to + // x-taler-bank. + talerBankHostnames?: string[]; + } diff --git a/core/wallet-core/deposits/get-max-deposit-amount.rst b/core/wallet-core/deposits/get-max-deposit-amount.rst @@ -0,0 +1,142 @@ +.. ts:op:: getMaxDepositAmount + :read-only: + + Query the maximum amount that can currently be deposited in the given + currency. + + **Request:** + + The request body must be a `GetMaxDepositAmountRequest` object. When + ``depositPaytoUri`` is omitted, wire-method eligibility, account + restrictions and wire fees cannot be reflected in the response. + + **Response:** + + On success, the result is a `GetMaxDepositAmountResponse` object. + + **Details:** + + The result distinguishes the maximum that can be deposited + immediately (``material``) from the maximum that also includes the + expected outputs of pending refresh operations (``available``). + ``exchangeDiagnostics`` gives, for every ready same-currency + exchange, the per-exchange maximums and the reasons why the exchange + cannot serve the deposit. + +.. ts:def:: GetMaxDepositAmountRequest + + interface GetMaxDepositAmountRequest { + // Currency to deposit. + currency: string; + + // Target bank account to deposit into. When omitted, wire-method + // eligibility, account restrictions and wire fees cannot be + // reflected in the response. + depositPaytoUri?: string; + + // Restrict the deposit to a certain scope. + restrictScope?: ScopeInfo; + } + +.. ts:def:: GetMaxDepositAmountResponse + + interface GetMaxDepositAmountResponse { + // Maximum that can be deposited immediately. + material: DepositMaximum; + + // Maximum including expected outputs of pending refresh + // operations. + available: DepositMaximum; + + // Eligibility and maximum amounts for every ready same-currency + // exchange. + exchangeDiagnostics: Record<string, DepositExchangeDiagnostics>; + } + +.. ts:def:: DepositMaximum + + // Maximum amounts and fees for one coherent deposit coin selection. + interface DepositMaximum { + // Gross target amount passed to checkDeposit or + // createDepositGroup. + instructedAmount: AmountString; + + // Total balance effect on the wallet: instructed amount plus fees + // paid by the customer and the cost of refreshing any change. + effectiveAmount: AmountString; + + // Amount expected to reach the destination account: instructed + // amount minus fees covered by the counterparty. + rawAmount: AmountString; + + // Total fees incurred by this deposit selection. + fees: DepositGroupFees; + } + +.. ts:def:: DepositExchangeDiagnostics + + interface DepositExchangeDiagnostics { + // Maximum that can be deposited immediately. + material: DepositMaximum; + + // Maximum including expected outputs of pending refresh + // operations. + available: DepositMaximum; + + // Eligibility failures, in deterministic evaluation order. + reasons: DepositEligibilityReason[]; + } + +.. ts:def:: DepositEligibilityReason + + // Reason why a ready, same-currency exchange cannot serve a deposit. + type DepositEligibilityReason = + | { type: "direct-deposit-disabled" } + | { type: "scope-restricted"; scopeInfo: ScopeInfo } + | { type: "wire-method-unsupported"; wireMethod: string } + | { type: "wire-fee-unavailable"; wireMethod: string } + | { + type: "deposit-account-restricted"; + wireMethod: string; + accountRestrictions: Record<string, AccountRestriction[]>; + }; + +.. ts:def:: DepositEligibilityReasonType + + type DepositEligibilityReasonType = + "direct-deposit-disabled" + | "scope-restricted" + | "wire-method-unsupported" + | "wire-fee-unavailable" + | "deposit-account-restricted"; + +.. ts:def:: AccountRestriction + + type AccountRestriction = + | RegexAccountRestriction + | DenyAllAccountRestriction; + +.. ts:def:: RegexAccountRestriction + + // Accounts interacting with this type of account restriction must + // have a payto://-URI matching the given regex. + interface RegexAccountRestriction { + type: "regex"; + + // Regular expression that the payto://-URI of the partner account + // must follow (posix-egrep, without support for character + // classes, GNU extensions, back-references or intervals). + payto_regex: string; + + // Hint for a human to understand the restriction. + human_hint: string; + + // Map from IETF BCP 47 language tags to localized human hints. + human_hint_i18n?: InternationalizedString; + } + +.. ts:def:: DenyAllAccountRestriction + + interface DenyAllAccountRestriction { + type: "deny"; + } diff --git a/core/wallet-core/diagnostics.rst b/core/wallet-core/diagnostics.rst @@ -1,102 +0,0 @@ -.. _wallet-op-getDiagnostics: - -**getDiagnostics** - -Get a diagnostics report about the state of the wallet, serialized as -a string in the requested format. - -**Request:** - - The request arguments must be a `GetDiagnosticsRequest` object. - -**Response:** - - On success, the result is a `GetDiagnosticsResponse`: the - diagnostics report serialized as a string. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - The report is privacy-scrubbed: transaction identifiers are replaced - by synthetic labels. It contains the wallet-core version, the - database backend with record counts, the known exchanges, coins, - bank accounts and the newest transactions (at most - ``transactionLimit`` of them). The ``WALLET_CORE_API_BAD_REQUEST`` - error is raised when ``transactionLimit`` is not a non-negative - safe integer. - -.. ts:def:: GetDiagnosticsRequest - - interface GetDiagnosticsRequest { - // Serialized output format. Defaults to JSON. - format?: DiagnosticsFormat; - - // Maximum number of newest transactions to include. Defaults to 100. - transactionLimit?: number; - - // Information supplied by the wallet frontend invoking wallet-core. - frontendInfo?: DiagnosticsFrontendInfo; - } - -.. ts:def:: DiagnosticsFormat - - type DiagnosticsFormat = "json" | "yaml"; - -.. ts:def:: DiagnosticsFrontendInfo - - interface DiagnosticsFrontendInfo { - name: string; - version: string; - platform?: string; - } - -.. ts:def:: GetDiagnosticsResponse - - // A serialized diagnostics report in the requested format, returned - // as a string so that clients can save it without depending on the - // report's internal schema. - type GetDiagnosticsResponse = string; - - -.. _wallet-op-getActiveTasks: - -**getActiveTasks** - -Get the wallet's currently active background tasks and their retry -state. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `GetActiveTasksResponse` object. - -**Details:** - - Tasks are wallet-core's background retry loops, for example for - pending withdrawals or payments. ``firstTry`` and ``nextTry`` give - the time of the first attempt and of the next scheduled retry; - ``lastError`` is the error of the most recent attempt, if any. - -.. ts:def:: GetActiveTasksResponse - - interface GetActiveTasksResponse { - tasks: ActiveTask[]; - } - -.. ts:def:: ActiveTask - - interface ActiveTask { - taskId: string; - transaction?: TransactionIdStr | undefined; - firstTry?: AbsoluteTime | undefined; - nextTry?: AbsoluteTime | undefined; - retryCounter?: number | undefined; - lastError?: TalerErrorDetail | undefined; - } diff --git a/core/wallet-core/diagnostics/get-active-tasks.rst b/core/wallet-core/diagnostics/get-active-tasks.rst @@ -0,0 +1,38 @@ +.. ts:op:: getActiveTasks + :read-only: + + Get the wallet's currently active background tasks and their retry + state. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `GetActiveTasksResponse` object. + + **Details:** + + Tasks are wallet-core's background retry loops, for example for + pending withdrawals or payments. ``firstTry`` and ``nextTry`` + give the time of the first attempt and of the next scheduled + retry; ``lastError`` is the error of the most recent attempt, if + any. + +.. ts:def:: GetActiveTasksResponse + + interface GetActiveTasksResponse { + tasks: ActiveTask[]; + } + +.. ts:def:: ActiveTask + + interface ActiveTask { + taskId: string; + transaction?: TransactionIdStr | undefined; + firstTry?: AbsoluteTime | undefined; + nextTry?: AbsoluteTime | undefined; + retryCounter?: number | undefined; + lastError?: TalerErrorDetail | undefined; + } diff --git a/core/wallet-core/diagnostics/get-diagnostics.rst b/core/wallet-core/diagnostics/get-diagnostics.rst @@ -0,0 +1,61 @@ +.. ts:op:: getDiagnostics + :read-only: + + Get a diagnostics report about the state of the wallet, serialized + as a string in the requested format. + + **Request:** + + The request arguments must be a `GetDiagnosticsRequest` object. + + **Response:** + + On success, the result is a `GetDiagnosticsResponse`: the + diagnostics report serialized as a string. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + The report is privacy-scrubbed: transaction identifiers are + replaced by synthetic labels. It contains the wallet-core + version, the database backend with record counts, the known + exchanges, coins, bank accounts and the newest transactions (at + most ``transactionLimit`` of them). The + ``WALLET_CORE_API_BAD_REQUEST`` error is raised when + ``transactionLimit`` is not a non-negative safe integer. + +.. ts:def:: GetDiagnosticsRequest + + interface GetDiagnosticsRequest { + // Serialized output format. Defaults to JSON. + format?: DiagnosticsFormat; + + // Maximum number of newest transactions to include. Defaults to 100. + transactionLimit?: number; + + // Information supplied by the wallet frontend invoking wallet-core. + frontendInfo?: DiagnosticsFrontendInfo; + } + +.. ts:def:: DiagnosticsFormat + + type DiagnosticsFormat = "json" | "yaml"; + +.. ts:def:: DiagnosticsFrontendInfo + + interface DiagnosticsFrontendInfo { + name: string; + version: string; + platform?: string; + } + +.. ts:def:: GetDiagnosticsResponse + + // A serialized diagnostics report in the requested format, returned + // as a string so that clients can save it without depending on the + // report's internal schema. + type GetDiagnosticsResponse = string; diff --git a/core/wallet-core/donau.rst b/core/wallet-core/donau.rst @@ -1,104 +0,0 @@ -.. _wallet-op-setDonau: - -**setDonau** - -Set the donation authority for this wallet. - -**Request:** - - The request arguments must be a `SetDonauRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The taxpayer ID is stored together with a salted hash of it (as - defined in LSD0013) that is later used to submit and query donation - receipts. Repeating the request with an unchanged ``donauBaseUrl`` - and ``taxPayerId`` is idempotent: the existing salt is kept. - -.. ts:def:: SetDonauRequest - - interface SetDonauRequest { - donauBaseUrl: string; - taxPayerId: string; - } - - -.. _wallet-op-getDonau: - -**getDonau** - -Get the currently configured donation authority for this wallet. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `GetDonauResponse` object. The - ``currentDonauInfo`` field is undefined if no donation authority - has been configured. - -.. ts:def:: GetDonauResponse - - interface GetDonauResponse { - currentDonauInfo: - | { - donauBaseUrl: string; - taxPayerId: string; - } - | undefined; - } - - -.. _wallet-op-getDonauStatements: - -**getDonauStatements** - -Get a list of donation statements for this wallet. Both donation -statements for the currently configured donation authority as well as -past configurations (if they exist) are returned. - -**Request:** - - The request arguments must be a `GetDonauStatementsRequest` object. - -**Response:** - - On success, the result is a `GetDonauStatementsResponse` object. - -**Details:** - - Before statements are queried, the wallet submits all pending - donation receipts to the respective donation authority. A statement - is then requested from every donation authority (or only from the - one named by ``donauBaseUrl``, if given) for each year with - submitted receipts; years for which the authority has not yet issued - a statement contribute no entry to the result. - -.. ts:def:: GetDonauStatementsRequest - - interface GetDonauStatementsRequest { - donauBaseUrl?: string; - } - -.. ts:def:: GetDonauStatementsResponse - - interface GetDonauStatementsResponse { - statements: DonauStatementItem[]; - } - -.. ts:def:: DonauStatementItem - - interface DonauStatementItem { - total: AmountString; - year: number; - legalDomain: string; - uri: string; - donationStatementSig: EddsaSignatureString; - donauPub: EddsaPublicKeyString; - } diff --git a/core/wallet-core/donau/get-donau-statements.rst b/core/wallet-core/donau/get-donau-statements.rst @@ -0,0 +1,51 @@ +.. ts:op:: getDonauStatements + + Get a list of donation statements for this wallet. Both donation + statements for the currently configured donation authority as well + as past configurations (if they exist) are returned. + + **Request:** + + The request arguments must be a `GetDonauStatementsRequest` + object. + + **Response:** + + On success, the result is a `GetDonauStatementsResponse` object. + + **Side effects:** + + Submits all pending donation receipts to the respective donation + authority over the network and marks them as submitted in the + wallet database, emitting a balance-change notification, before + the statements are fetched. + + **Details:** + + A statement is requested from every donation authority (or only + from the one named by ``donauBaseUrl``, if given) for each year + with submitted receipts; years for which the authority has not yet + issued a statement contribute no entry to the result. + +.. ts:def:: GetDonauStatementsRequest + + interface GetDonauStatementsRequest { + donauBaseUrl?: string; + } + +.. ts:def:: GetDonauStatementsResponse + + interface GetDonauStatementsResponse { + statements: DonauStatementItem[]; + } + +.. ts:def:: DonauStatementItem + + interface DonauStatementItem { + total: AmountString; + year: number; + legalDomain: string; + uri: string; + donationStatementSig: EddsaSignatureString; + donauPub: EddsaPublicKeyString; + } diff --git a/core/wallet-core/donau/get-donau.rst b/core/wallet-core/donau/get-donau.rst @@ -0,0 +1,25 @@ +.. ts:op:: getDonau + :read-only: + + Get the currently configured donation authority for this wallet. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `GetDonauResponse` object. The + ``currentDonauInfo`` field is undefined if no donation authority + has been configured. + +.. ts:def:: GetDonauResponse + + interface GetDonauResponse { + currentDonauInfo: + | { + donauBaseUrl: string; + taxPayerId: string; + } + | undefined; + } diff --git a/core/wallet-core/donau/set-donau.rst b/core/wallet-core/donau/set-donau.rst @@ -0,0 +1,31 @@ +.. ts:op:: setDonau + + Set the donation authority for this wallet. + + **Request:** + + The request arguments must be a `SetDonauRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Writes the donation authority configuration to the wallet + database, replacing any previously configured authority. + + **Details:** + + The taxpayer ID is stored together with a salted hash of it (as + defined in LSD0013) that is later used to submit and query + donation receipts. Repeating the request with an unchanged + ``donauBaseUrl`` and ``taxPayerId`` is idempotent: the existing + salt is kept. + +.. ts:def:: SetDonauRequest + + interface SetDonauRequest { + donauBaseUrl: string; + taxPayerId: string; + } diff --git a/core/wallet-core/exchanges.rst b/core/wallet-core/exchanges.rst @@ -1,940 +0,0 @@ -.. _wallet-op-addExchange: - -**addExchange** - -Add an exchange to the wallet, or force an update of the exchange entry. - -**Request:** - - The request arguments must be an `AddExchangeRequest` object. - -**Response:** - - On success, the result is an `AddExchangeResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``, ``WALLET_EXCHANGE_UNAVAILABLE``, - ``WALLET_EXCHANGE_SIGNATURE_INVALID``, ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - The ``uri`` is either an http(s) exchange base URL or a - ``taler://add-exchange/`` URI. An http(s) URL must already be - canonical, unless ``allowCompletion`` is set; in that case the wallet - tries to complete the URL as with - :ref:`completeExchangeBaseUrl <wallet-op-completeExchangeBaseUrl>` and - fails if completion is not possible. The wallet fetches the - exchange's key data before the request succeeds. Unless ``ephemeral`` - is set, the exchange is marked as explicitly added by the user. - -.. ts:def:: AddExchangeRequest - - interface AddExchangeRequest { - // Either an http(s) exchange base URL or - // a taler://add-exchange/ URI. - uri?: string; - - // Only ephemerally add the exchange. - ephemeral?: boolean; - - // Allow passing incomplete URLs. The wallet will try to complete - // the URL and throw an error if completion is not possible. - allowCompletion?: boolean; - - // Deprecated: start a forced exchange update with a separate - // updateExchangeEntry call instead. - forceUpdate?: boolean; - - // Deprecated: use the uri field instead. - exchangeBaseUrl?: string; - - // Correlates progress notifications and allows cancellation. - progressToken?: string; - } - -.. ts:def:: AddExchangeResponse - - interface AddExchangeResponse { - // Base URL of the exchange that was added to the wallet. - exchangeBaseUrl: string; - } - - -.. _wallet-op-listExchanges: - -**listExchanges** - -List exchanges known to the wallet. - -**Request:** - - The request arguments must be a `ListExchangesRequest` object. - Omitted filter fields do not restrict the result. - -**Response:** - - On success, the result is an `ExchangesListResponse` object. - -.. ts:def:: ListExchangesRequest - - interface ListExchangesRequest { - // Filter results to only include exchanges in the given scope. - filterByScope?: ScopeInfo; - - // Filter results to only include exchanges - // with the given status. - filterByExchangeEntryStatus?: ExchangeEntryStatus; - - // Filter results to only include exchanges with the - // given type. - filterByType?: ExchangeType; - } - -.. ts:def:: ExchangeType - - type ExchangeType = "demo" | "prod"; - -.. ts:def:: ExchangesListResponse - - interface ExchangesListResponse { - exchanges: ExchangeListItem[]; - } - -.. ts:def:: ExchangeListItem - - // Info about an exchange entry in the wallet. - interface ExchangeListItem { - exchangeBaseUrl: string; - - source?: ExchangeEntrySource; - - masterPub: string | undefined; - - // Master public keys this exchange URL used before its current - // key. The array is sorted and does not include masterPub. - legacyMasterPubs: string[]; - - // Set when the exchange changed its key set and the user has not - // confirmed the change yet. Operations that send money to the - // exchange or disclose coin authorizations are refused while this - // is present. - unconfirmedKeyChange?: ExchangeKeyChangeInfo; - - currency: string; - - paytoUris: string[]; - - tosStatus: ExchangeTosStatus; - - exchangeEntryStatus: ExchangeEntryStatus; - - exchangeUpdateStatus: ExchangeUpdateStatus; - - ageRestrictionOptions: number[]; - - walletKycStatus?: ExchangeWalletKycStatus; - - walletKycReservePub?: string; - - walletKycAccessToken?: string; - - walletKycUrl?: string; - - // Threshold that we've requested to satisfy. - walletKycRequestedThreshold?: string; - - // P2P payments are disabled with this exchange - // (e.g. because no global fees are configured). - peerPaymentsDisabled: boolean; - - directDepositsDisabled: boolean; - - // Set to true if this exchange doesn't charge any fees. - noFees: boolean; - - // Most general scope that the exchange is a part of. - scopeInfo: ScopeInfo; - - // Instructs wallets to use certain bank-specific language (for - // buttons) and/or other UI/UX customization for compliance with - // the rules of that bank. - bankComplianceLanguage?: string; - - lastUpdateTimestamp: TalerPreciseTimestamp | undefined; - - // Most recent successful withdrawal through this exchange. - lastWithdrawal?: TalerPreciseTimestamp; - - // Information about the last error that occurred when trying - // to update the exchange info. - lastUpdateErrorInfo?: OperationErrorInfo; - - // Currency spec for the currency offered by the exchange. - currencySpec: CurrencySpecification; - } - -.. ts:def:: ExchangeKeyChangeInfo - - // An exchange that changed its key set, pending the user's - // confirmation. The wallet has already adopted the new key set, so - // the entry works and the older coins stay spendable; withdrawing is - // withheld until the change is confirmed. - interface ExchangeKeyChangeInfo { - // Master public key the exchange now uses, and the wallet now - // trusts. - currentMasterPub: string; - - currentCurrency: string; - - // Master public key the wallet's older funds were issued under. - supersededMasterPub: string; - - supersededCurrency: string; - - // Whether the new key set still advertises denominations the - // wallet holds coins of. False means the exchange does not offer - // to settle the older coins at all; true is only the exchange's - // claim, not proof of continuity. - sharesDenominations: boolean; - - firstSeen: TalerPreciseTimestamp; - } - -.. ts:def:: OperationErrorInfo - - interface OperationErrorInfo { - error: TalerErrorDetail; - } - -.. ts:def:: ExchangeTosStatus - - type ExchangeTosStatus = - | "pending" - | "proposed" - | "accepted" - | "missing-tos"; - -.. ts:def:: ExchangeEntryStatus - - type ExchangeEntryStatus = - | "preset" - | "ephemeral" - | "used"; - -.. ts:def:: ExchangeEntrySource - - // How an exchange entry became known to the wallet. - type ExchangeEntrySource = - | "builtin" - | "user" - | "discovered" - | "unknown"; - -.. ts:def:: ExchangeUpdateStatus - - type ExchangeUpdateStatus = - | "initial" - | "initial-update" - | "suspended" - | "unavailable-update" - | "ready" - | "ready-update" - | "outdated-update"; - - -.. _wallet-op-listWithdrawalExchangeCandidates: - -**listWithdrawalExchangeCandidates** - -List exchanges suitable for presentation in a withdrawal chooser. - -**Request:** - - The request arguments must be a `ListWithdrawalExchangeCandidatesRequest` - object. Omitted fields use their documented defaults. - -**Response:** - - On success, the result is a `ListWithdrawalExchangeCandidatesResponse` - object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - Candidates are taken from the wallet's exchange entries and from the - builtin exchange list; ephemeral entries and entries with unknown - currency are excluded. Demo and test exchanges are only included when - ``withDemo`` or ``withTest`` are set. Combining ``presetOnly`` with - ``withBuiltin: false`` is rejected with - ``WALLET_CORE_API_BAD_REQUEST``. Exchanges recently used for a - withdrawal are sorted first, and ``recommendationReasons`` states why - each candidate is suggested. - -.. ts:def:: GetDefaultExchangesRequest - - interface GetDefaultExchangesRequest { - // Only return production exchanges from the builtin exchange list. - // Cannot be combined with withBuiltin: false. - presetOnly?: boolean; - - // Include exchanges from the builtin list. Defaults to true. - withBuiltin?: boolean; - - // Include demo exchanges from the builtin list. Defaults to false. - withDemo?: boolean; - - // Include test exchanges from the builtin list. Defaults to false. - withTest?: boolean; - } - -.. ts:def:: ListWithdrawalExchangeCandidatesRequest - - type ListWithdrawalExchangeCandidatesRequest = - GetDefaultExchangesRequest; - -.. ts:def:: ListWithdrawalExchangeCandidatesResponse - - interface ListWithdrawalExchangeCandidatesResponse { - candidates: WithdrawalExchangeCandidate[]; - } - -.. ts:def:: WithdrawalExchangeCandidate - - interface WithdrawalExchangeCandidate { - // A taler://withdraw-exchange URI for the exchange. - talerUri: string; - - exchangeBaseUrl: string; - - currency: string; - - currencySpec: CurrencySpecification; - - exchangeEntryStatus: ExchangeEntryStatus; - - exchangeUpdateStatus: ExchangeUpdateStatus; - - source: ExchangeEntrySource; - - recommendationReasons: ExchangeRecommendationReason[]; - - lastWithdrawal?: TalerPreciseTimestamp; - } - -.. ts:def:: ExchangeRecommendationReason - - type ExchangeRecommendationReason = - | "preset" - | "user-added" - | "previous-withdrawal" - | "previously-used"; - - -.. _wallet-op-getDefaultExchanges: - -**getDefaultExchanges** - -This operation is **deprecated**. Use -:ref:`listWithdrawalExchangeCandidates -<wallet-op-listWithdrawalExchangeCandidates>` instead. - -List the default exchanges offered to the user for withdrawing. - -**Request:** - - The request arguments must be a `GetDefaultExchangesRequest` object, - as for :ref:`listWithdrawalExchangeCandidates - <wallet-op-listWithdrawalExchangeCandidates>`. - -**Response:** - - On success, the result is a `GetDefaultExchangesResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - The result is a reduced view of the withdrawal exchange candidates, - carrying only the withdrawal URI, currency and currency - specification of each candidate. - -.. ts:def:: GetDefaultExchangesResponse - - interface GetDefaultExchangesResponse { - defaultExchanges: { - // A taler://withdraw-exchange URI for the exchange. - talerUri: string; - - // Currency offered by the exchange. - currency: string; - - // Currency spec for the currency offered by the exchange. - currencySpec: CurrencySpecification; - }[]; - } - - -.. _wallet-op-getExchangeEntryByUrl: - -**getExchangeEntryByUrl** - -Get the wallet's exchange entry for an exchange base URL. - -**Request:** - - The request arguments must be a `GetExchangeEntryByUrlRequest` object. - -**Response:** - - On success, the result is a `GetExchangeEntryByUrlResponse` object, - which is an alias for `ExchangeListItem` (see - :ref:`listExchanges <wallet-op-listExchanges>`). - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``. - -.. ts:def:: GetExchangeEntryByUrlRequest - - interface GetExchangeEntryByUrlRequest { - exchangeBaseUrl: string; - } - -.. ts:def:: GetExchangeEntryByUrlResponse - - type GetExchangeEntryByUrlResponse = ExchangeListItem; - - -.. _wallet-op-updateExchangeEntry: - -**updateExchangeEntry** - -Update an exchange entry. - -Only starts updating the exchange entry. After this request finishes, -it is not guaranteed that the exchange entry has been updated. Use -notifications and the :ref:`listExchanges <wallet-op-listExchanges>` -request to check the status. - -**Request:** - - The request arguments must be an `UpdateExchangeEntryRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, - ``WALLET_EXCHANGE_ENTRY_UPDATE_CONFLICT``, - ``WALLET_EXCHANGE_UNAVAILABLE``. - -**Details:** - - Setting ``force`` starts the update even when the entry was recently - updated or the exchange is currently marked unavailable. - -.. ts:def:: UpdateExchangeEntryRequest - - interface UpdateExchangeEntryRequest { - exchangeBaseUrl: string; - - // Force the update even if the entry was recently updated or - // the exchange is currently marked unavailable. - force?: boolean; - } - - -.. _wallet-op-getExchangeResources: - -**getExchangeResources** - -Check whether the wallet still holds resources associated with an -exchange. - -**Request:** - - The request arguments must be a `GetExchangeResourcesRequest` object. - -**Response:** - - On success, the result is a `GetExchangeResourcesResponse` object. - -**Details:** - - Resources are the coins and withdrawal groups (including internal - withdrawals of peer transactions) associated with the exchange; - ``hasResources`` is true if any of them exist. Clients can use this - to decide whether :ref:`deleteExchange <wallet-op-deleteExchange>` - requires explicit user confirmation. - -.. ts:def:: GetExchangeResourcesRequest - - interface GetExchangeResourcesRequest { - exchangeBaseUrl: string; - } - -.. ts:def:: GetExchangeResourcesResponse - - interface GetExchangeResourcesResponse { - hasResources: boolean; - } - - -.. _wallet-op-completeExchangeBaseUrl: - -**completeExchangeBaseUrl** - -Try to complete a partial URL into the canonical base URL of an -exchange. - -**Request:** - - The request arguments must be a `CompleteBaseUrlRequest` object. - -**Response:** - - On success, the result is a `CompleteBaseUrlResult` object. - -**Details:** - - The wallet derives candidate base URLs from ``url`` and probes them, - returning the first candidate that answers like an exchange. A - failure to complete is reported in-band via the ``status`` field of - the result, not with an error response: ``bad-syntax`` when the URL - is too malformed to be completed, ``bad-network`` when no candidate - could be reached and ``bad-exchange`` when the endpoint is reachable - but is not an exchange. In the failure case, ``suggestions`` may - list base URLs of exchanges known to the wallet that look similar to - what was typed. - -.. ts:def:: CompleteBaseUrlRequest - - interface CompleteBaseUrlRequest { - url: string; - - // Correlates progress notifications and allows cancellation. - progressToken?: string; - } - -.. ts:def:: CompleteBaseUrlResult - - type CompleteBaseUrlResult = - | { - // ok: completion is a proper exchange - status: "ok"; - - // Completed exchange base URL, if completion was possible - completion: string; - } - | { - // bad-syntax: url is so badly malformed, it can't be completed - // bad-network: syntax okay, but exchange can't be reached - // bad-exchange: syntax and network okay, but not talking to - // an exchange - status: "bad-syntax" | "bad-network" | "bad-exchange"; - - // Error details in case status is not "ok" - error: TalerErrorDetail; - - // Base URLs of exchanges known to the wallet whose host looks - // like what the user meant to type, most likely first. - // Absent when the wallet does not know anything similar. - suggestions?: string[]; - }; - - -.. _wallet-op-deleteExchange: - -**deleteExchange** - -Delete an exchange and its associated resources. - -**Request:** - - The request arguments must be a `DeleteExchangeRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, ``WALLET_EXCHANGE_ENTRY_USED``. - -**Details:** - - The operation fails with ``WALLET_EXCHANGE_ENTRY_USED`` when the - exchange still has associated resources (see - :ref:`getExchangeResources <wallet-op-getExchangeResources>`), unless - ``purge`` is set. Some transactions related to the exchange - (payments, peer payments and refreshes) are kept even when purging. - -.. ts:def:: DeleteExchangeRequest - - interface DeleteExchangeRequest { - exchangeBaseUrl: string; - - // Delete the exchange even if it's in use. - purge?: boolean; - } - - -.. _wallet-op-purgeExchangeLegacyKeys: - -**purgeExchangeLegacyKeys** - -Purge every non-current key set retained for an exchange URL. - -**Request:** - - The request arguments must be a `PurgeExchangeLegacyKeysRequest` - object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - Removes the coins, denominations and other stored data issued under - superseded master public keys of the exchange; withdrawals of purged - coins are marked as legacy. The ``currentMasterPub`` must be the - current master key observed by the client before confirming the - purge: the operation fails atomically with - ``WALLET_CORE_API_BAD_REQUEST`` if the exchange has changed keys - since, so a key rotation between review and execution cannot turn - the key the user just reviewed into another key that is silently - purged. - -.. ts:def:: PurgeExchangeLegacyKeysRequest - - interface PurgeExchangeLegacyKeysRequest { - exchangeBaseUrl: string; - - // Current master key observed by the client before confirming the - // purge. The operation fails atomically if the exchange has - // changed keys since. - currentMasterPub: string; - } - - -.. _wallet-op-confirmExchangeKeyChange: - -**confirmExchangeKeyChange** - -Confirm that the exchange's changed key set is legitimate. - -The wallet has already adopted the changed key set; this releases the -operations that send money to the exchange, which are withheld until -the user has had a chance to notice that the key changed. - -**Request:** - - The request arguments must be a `ConfirmExchangeKeyChangeRequest` - object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_NO_KEY_CHANGE_PENDING``, - ``WALLET_EXCHANGE_KEY_CHANGE_MISMATCH``. - -**Details:** - - ``currentMasterPub`` must be the master public key the exchange - currently uses, so that a UI showing a stale key change cannot - confirm a different one than the user was looking at. - -.. ts:def:: ConfirmExchangeKeyChangeRequest - - interface ConfirmExchangeKeyChangeRequest { - exchangeBaseUrl: string; - - // Master public key the exchange now uses. Required, so that a - // UI showing a stale key change cannot confirm a different one - // than the user was looking at. - currentMasterPub: string; - } - - -.. _wallet-op-setExchangeTosAccepted: - -**setExchangeTosAccepted** - -Mark the current version of the exchange's terms of service as accepted -by the user. - -**Request:** - - The request arguments must be an `AcceptExchangeTosRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The accepted version is the current ToS version observed by the - wallet, as reported by :ref:`getExchangeTos <wallet-op-getExchangeTos>`. - -.. ts:def:: AcceptExchangeTosRequest - - interface AcceptExchangeTosRequest { - exchangeBaseUrl: string; - } - - -.. _wallet-op-setExchangeTosForgotten: - -**setExchangeTosForgotten** - -Forget the acceptance of the exchange's terms of service, marking them -as not accepted. - -**Request:** - - The request arguments must be an `AcceptExchangeTosRequest` object - (see :ref:`setExchangeTosAccepted <wallet-op-setExchangeTosAccepted>`). - -**Response:** - - On success, the result is an empty object. - - -.. _wallet-op-getExchangeTos: - -**getExchangeTos** - -Get the current terms of service of an exchange. - -**Request:** - - The request arguments must be a `GetExchangeTosRequest` object. - -**Response:** - - On success, the result is a `GetExchangeTosResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``. - -**Details:** - - The wallet downloads the terms of service from the exchange, using - ``acceptedFormat`` and ``acceptLanguage`` for content negotiation, - and updates its stored current ToS version tag. If the exchange - does not provide terms of service, ``tosStatus`` is ``"missing-tos"`` - and the content fields carry placeholder values. - -.. ts:def:: GetExchangeTosRequest - - interface GetExchangeTosRequest { - exchangeBaseUrl: string; - - // Acceptable content types for the ToS text, used for content - // negotiation with the exchange. - acceptedFormat?: string[]; - - // Preferred language for the ToS text. - acceptLanguage?: string; - - // Correlates progress notifications and allows cancellation. - progressToken?: string; - } - -.. ts:def:: GetExchangeTosResult - - interface GetExchangeTosResult { - // Markdown version of the current ToS. - content: string; - - // Version tag of the current ToS. - currentEtag: string; - - // Version tag of the last ToS that the user has accepted, if any. - acceptedEtag: string | undefined; - - // Accepted content type - contentType: string; - - // Language of the returned content. If missing, language is - // unknown. - contentLanguage: string | undefined; - - // Available languages as advertised by the exchange. - tosAvailableLanguages: string[]; - - tosStatus: ExchangeTosStatus; - } - - -.. _wallet-op-getExchangeDetailedInfo: - -**getExchangeDetailedInfo** - -Get detailed information about an exchange, including a timeline for -the fees charged by the exchange. - -**Request:** - - The request arguments must be a `GetExchangeDetailedInfoRequest` - object. - -**Response:** - - On success, the result is an `ExchangeDetailedResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``. - -**Details:** - - The ``denomFees``, ``transferFees`` and ``globalFees`` of the result - are timelines: each `FeeDescription` covers the time range from - ``from`` to ``until`` during which the fee applies. - -.. ts:def:: GetExchangeDetailedInfoRequest - - interface GetExchangeDetailedInfoRequest { - exchangeBaseUrl: string; - } - -.. ts:def:: ExchangeDetailedResponse - - interface ExchangeDetailedResponse { - exchange: ExchangeFullDetails; - } - -.. ts:def:: ExchangeFullDetails - - interface ExchangeFullDetails { - exchangeBaseUrl: string; - - currency: string; - - paytoUris: string[]; - - auditors: ExchangeAuditor[]; - - wireInfo: WireInfo; - - denomFees: DenomOperationMap<FeeDescription[]>; - - transferFees: Record<string, FeeDescription[]>; - - globalFees: FeeDescription[]; - } - -.. ts:def:: DenomOperation - - type DenomOperation = "deposit" | "withdraw" | "refresh" | "refund"; - -.. ts:def:: DenomOperationMap - - type DenomOperationMap<T> = { [op in DenomOperation]: T }; - -.. ts:def:: FeeDescription - - interface FeeDescription { - group: string; - - from: AbsoluteTime; - - until: AbsoluteTime; - - fee?: AmountString; - } - -.. ts:def:: WireInfo - - interface WireInfo { - feesForType: WireFeeMap; - - accounts: ExchangeWireAccount[]; - } - -.. ts:def:: WireFeeMap - - type WireFeeMap = { [wireMethod: string]: WireFee[] }; - -.. ts:def:: WireFee - - // Wire fee for one wire method - interface WireFee { - // Fee for wire transfers. - wireFee: AmountString; - - // Fees to close and refund a reserve. - closingFee: AmountString; - - // Start date of the fee. - startStamp: TalerProtocolTimestamp; - - // End date of the fee. - endStamp: TalerProtocolTimestamp; - - // Signature made by the exchange master key. - sig: string; - } - - -.. _wallet-op-startExchangeWalletKyc: - -**startExchangeWalletKyc** - -Start the wallet KYC process at an exchange, requesting authorization -to hold funds up to the given amount threshold. - -**Request:** - - The request arguments must be a `StartExchangeWalletKycRequest` - object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The request only initiates the KYC process; the wallet then drives it - in the background and reports status changes via notifications. If a - KYC threshold of at least ``amount`` was already granted or is - already being requested, the operation does nothing. Once started, - the exchange entry is marked as used, as the wallet keeps an account - at the exchange. - -.. ts:def:: StartExchangeWalletKycRequest - - interface StartExchangeWalletKycRequest { - exchangeBaseUrl: string; - - // Amount threshold that the KYC process should authorize. - amount: AmountString; - } diff --git a/core/wallet-core/exchanges/add-exchange.rst b/core/wallet-core/exchanges/add-exchange.rst @@ -0,0 +1,67 @@ +.. ts:op:: addExchange + + Add an exchange to the wallet, or force an update of the exchange + entry. + + **Request:** + + The request arguments must be an `AddExchangeRequest` object. + + **Response:** + + On success, the result is an `AddExchangeResponse` object. + + **Side effects:** + + Adds or force-updates the exchange entry. The exchange's keys and + wire information are fetched over the network, and notifications + are emitted. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``, ``WALLET_EXCHANGE_UNAVAILABLE``, + ``WALLET_EXCHANGE_SIGNATURE_INVALID``, ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + The ``uri`` is either an http(s) exchange base URL or a + ``taler://add-exchange/`` URI. An http(s) URL must already be + canonical, unless ``allowCompletion`` is set; in that case the + wallet tries to complete the URL as with + :ts:op:`completeExchangeBaseUrl` and fails if completion is not + possible. The wallet fetches the exchange's key data before the + request succeeds. Unless ``ephemeral`` is set, the exchange is + marked as explicitly added by the user. + +.. ts:def:: AddExchangeRequest + + interface AddExchangeRequest { + // Either an http(s) exchange base URL or + // a taler://add-exchange/ URI. + uri?: string; + + // Only ephemerally add the exchange. + ephemeral?: boolean; + + // Allow passing incomplete URLs. The wallet will try to complete + // the URL and throw an error if completion is not possible. + allowCompletion?: boolean; + + // Deprecated: start a forced exchange update with a separate + // updateExchangeEntry call instead. + forceUpdate?: boolean; + + // Deprecated: use the uri field instead. + exchangeBaseUrl?: string; + + // Correlates progress notifications and allows cancellation. + progressToken?: string; + } + +.. ts:def:: AddExchangeResponse + + interface AddExchangeResponse { + // Base URL of the exchange that was added to the wallet. + exchangeBaseUrl: string; + } diff --git a/core/wallet-core/exchanges/complete-exchange-base-url.rst b/core/wallet-core/exchanges/complete-exchange-base-url.rst @@ -0,0 +1,64 @@ +.. ts:op:: completeExchangeBaseUrl + + Try to complete a partial URL into the canonical base URL of an + exchange. + + **Request:** + + The request arguments must be a `CompleteBaseUrlRequest` object. + + **Response:** + + On success, the result is a `CompleteBaseUrlResult` object. + + **Side effects:** + + Probes candidate base URLs over the network until one validates as + an exchange. + + **Details:** + + The wallet derives candidate base URLs from ``url`` and probes + them, returning the first candidate that answers like an exchange. + A failure to complete is reported in-band via the ``status`` field + of the result, not with an error response: ``bad-syntax`` when the + URL is too malformed to be completed, ``bad-network`` when no + candidate could be reached and ``bad-exchange`` when the endpoint + is reachable but is not an exchange. In the failure case, + ``suggestions`` may list base URLs of exchanges known to the wallet + that look similar to what was typed. + +.. ts:def:: CompleteBaseUrlRequest + + interface CompleteBaseUrlRequest { + url: string; + + // Correlates progress notifications and allows cancellation. + progressToken?: string; + } + +.. ts:def:: CompleteBaseUrlResult + + type CompleteBaseUrlResult = + | { + // ok: completion is a proper exchange + status: "ok"; + + // Completed exchange base URL, if completion was possible + completion: string; + } + | { + // bad-syntax: url is so badly malformed, it can't be completed + // bad-network: syntax okay, but exchange can't be reached + // bad-exchange: syntax and network okay, but not talking to + // an exchange + status: "bad-syntax" | "bad-network" | "bad-exchange"; + + // Error details in case status is not "ok" + error: TalerErrorDetail; + + // Base URLs of exchanges known to the wallet whose host looks + // like what the user meant to type, most likely first. + // Absent when the wallet does not know anything similar. + suggestions?: string[]; + }; diff --git a/core/wallet-core/exchanges/confirm-exchange-key-change.rst b/core/wallet-core/exchanges/confirm-exchange-key-change.rst @@ -0,0 +1,44 @@ +.. ts:op:: confirmExchangeKeyChange + + Confirm that the exchange's changed key set is legitimate. + + The wallet has already adopted the changed key set; this releases + the operations that send money to the exchange, which are withheld + until the user has had a chance to notice that the key changed. + + **Request:** + + The request arguments must be a `ConfirmExchangeKeyChangeRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Records the user's confirmation of the exchange's changed keys and + releases the withheld operations that send money to the exchange. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_NO_KEY_CHANGE_PENDING``, + ``WALLET_EXCHANGE_KEY_CHANGE_MISMATCH``. + + **Details:** + + ``currentMasterPub`` must be the master public key the exchange + currently uses, so that a UI showing a stale key change cannot + confirm a different one than the user was looking at. + +.. ts:def:: ConfirmExchangeKeyChangeRequest + + interface ConfirmExchangeKeyChangeRequest { + exchangeBaseUrl: string; + + // Master public key the exchange now uses. Required, so that a + // UI showing a stale key change cannot confirm a different one + // than the user was looking at. + currentMasterPub: string; + } diff --git a/core/wallet-core/exchanges/delete-exchange.rst b/core/wallet-core/exchanges/delete-exchange.rst @@ -0,0 +1,37 @@ +.. ts:op:: deleteExchange + + Delete an exchange and its associated resources. + + **Request:** + + The request arguments must be a `DeleteExchangeRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes the exchange and its associated resources. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, ``WALLET_EXCHANGE_ENTRY_USED``. + + **Details:** + + The operation fails with ``WALLET_EXCHANGE_ENTRY_USED`` when the + exchange still has associated resources (see + :ts:op:`getExchangeResources`), unless ``purge`` is set. Some + transactions related to the exchange (payments, peer payments and + refreshes) are kept even when purging. + +.. ts:def:: DeleteExchangeRequest + + interface DeleteExchangeRequest { + exchangeBaseUrl: string; + + // Delete the exchange even if it's in use. + purge?: boolean; + } diff --git a/core/wallet-core/exchanges/get-default-exchanges.rst b/core/wallet-core/exchanges/get-default-exchanges.rst @@ -0,0 +1,60 @@ +.. ts:op:: getDefaultExchanges + :read-only: + :deprecated: + + This operation is **deprecated**. Use + :ts:op:`listWithdrawalExchangeCandidates` instead. + + List the default exchanges offered to the user for withdrawing. + + **Request:** + + The request arguments must be a `GetDefaultExchangesRequest` + object, as for :ts:op:`listWithdrawalExchangeCandidates`. + + **Response:** + + On success, the result is a `GetDefaultExchangesResponse` object. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + The result is a reduced view of the withdrawal exchange + candidates, carrying only the withdrawal URI, currency and + currency specification of each candidate. + +.. ts:def:: GetDefaultExchangesRequest + + interface GetDefaultExchangesRequest { + // Only return production exchanges from the builtin exchange list. + // Cannot be combined with withBuiltin: false. + presetOnly?: boolean; + + // Include exchanges from the builtin list. Defaults to true. + withBuiltin?: boolean; + + // Include demo exchanges from the builtin list. Defaults to false. + withDemo?: boolean; + + // Include test exchanges from the builtin list. Defaults to false. + withTest?: boolean; + } + +.. ts:def:: GetDefaultExchangesResponse + + interface GetDefaultExchangesResponse { + defaultExchanges: { + // A taler://withdraw-exchange URI for the exchange. + talerUri: string; + + // Currency offered by the exchange. + currency: string; + + // Currency spec for the currency offered by the exchange. + currencySpec: CurrencySpecification; + }[]; + } diff --git a/core/wallet-core/exchanges/get-exchange-detailed-info.rst b/core/wallet-core/exchanges/get-exchange-detailed-info.rst @@ -0,0 +1,109 @@ +.. ts:op:: getExchangeDetailedInfo + :read-only: + + Get detailed information about an exchange, including a timeline + for the fees charged by the exchange. + + **Request:** + + The request arguments must be a `GetExchangeDetailedInfoRequest` + object. + + **Response:** + + On success, the result is an `ExchangeDetailedResponse` object. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``. + + **Details:** + + The ``denomFees``, ``transferFees`` and ``globalFees`` of the + result are timelines: each `FeeDescription` covers the time range + from ``from`` to ``until`` during which the fee applies. + +.. ts:def:: GetExchangeDetailedInfoRequest + + interface GetExchangeDetailedInfoRequest { + exchangeBaseUrl: string; + } + +.. ts:def:: ExchangeDetailedResponse + + interface ExchangeDetailedResponse { + exchange: ExchangeFullDetails; + } + +.. ts:def:: ExchangeFullDetails + + interface ExchangeFullDetails { + exchangeBaseUrl: string; + + currency: string; + + paytoUris: string[]; + + auditors: ExchangeAuditor[]; + + wireInfo: WireInfo; + + denomFees: DenomOperationMap<FeeDescription[]>; + + transferFees: Record<string, FeeDescription[]>; + + globalFees: FeeDescription[]; + } + +.. ts:def:: DenomOperation + + type DenomOperation = "deposit" | "withdraw" | "refresh" | "refund"; + +.. ts:def:: DenomOperationMap + + type DenomOperationMap<T> = { [op in DenomOperation]: T }; + +.. ts:def:: FeeDescription + + interface FeeDescription { + group: string; + + from: AbsoluteTime; + + until: AbsoluteTime; + + fee?: AmountString; + } + +.. ts:def:: WireInfo + + interface WireInfo { + feesForType: WireFeeMap; + + accounts: ExchangeWireAccount[]; + } + +.. ts:def:: WireFeeMap + + type WireFeeMap = { [wireMethod: string]: WireFee[] }; + +.. ts:def:: WireFee + + // Wire fee for one wire method + interface WireFee { + // Fee for wire transfers. + wireFee: AmountString; + + // Fees to close and refund a reserve. + closingFee: AmountString; + + // Start date of the fee. + startStamp: TalerProtocolTimestamp; + + // End date of the fee. + endStamp: TalerProtocolTimestamp; + + // Signature made by the exchange master key. + sig: string; + } diff --git a/core/wallet-core/exchanges/get-exchange-entry-by-url.rst b/core/wallet-core/exchanges/get-exchange-entry-by-url.rst @@ -0,0 +1,164 @@ +.. ts:op:: getExchangeEntryByUrl + :read-only: + + Get the wallet's exchange entry for an exchange base URL. + + **Request:** + + The request arguments must be a `GetExchangeEntryByUrlRequest` + object. + + **Response:** + + On success, the result is a `GetExchangeEntryByUrlResponse` object, + which is an alias for `ExchangeListItem` (see + :ts:op:`listExchanges`). + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``. + +.. ts:def:: ExchangeListItem + + // Info about an exchange entry in the wallet. + interface ExchangeListItem { + exchangeBaseUrl: string; + + source?: ExchangeEntrySource; + + masterPub: string | undefined; + + // Master public keys this exchange URL used before its current + // key. The array is sorted and does not include masterPub. + legacyMasterPubs: string[]; + + // Set when the exchange changed its key set and the user has not + // confirmed the change yet. Operations that send money to the + // exchange or disclose coin authorizations are refused while this + // is present. + unconfirmedKeyChange?: ExchangeKeyChangeInfo; + + currency: string; + + paytoUris: string[]; + + tosStatus: ExchangeTosStatus; + + exchangeEntryStatus: ExchangeEntryStatus; + + exchangeUpdateStatus: ExchangeUpdateStatus; + + ageRestrictionOptions: number[]; + + walletKycStatus?: ExchangeWalletKycStatus; + + walletKycReservePub?: string; + + walletKycAccessToken?: string; + + walletKycUrl?: string; + + // Threshold that we've requested to satisfy. + walletKycRequestedThreshold?: string; + + // P2P payments are disabled with this exchange + // (e.g. because no global fees are configured). + peerPaymentsDisabled: boolean; + + directDepositsDisabled: boolean; + + // Set to true if this exchange doesn't charge any fees. + noFees: boolean; + + // Most general scope that the exchange is a part of. + scopeInfo: ScopeInfo; + + // Instructs wallets to use certain bank-specific language (for + // buttons) and/or other UI/UX customization for compliance with + // the rules of that bank. + bankComplianceLanguage?: string; + + lastUpdateTimestamp: TalerPreciseTimestamp | undefined; + + // Most recent successful withdrawal through this exchange. + lastWithdrawal?: TalerPreciseTimestamp; + + // Information about the last error that occurred when trying + // to update the exchange info. + lastUpdateErrorInfo?: OperationErrorInfo; + + // Currency spec for the currency offered by the exchange. + currencySpec: CurrencySpecification; + } + +.. ts:def:: ExchangeKeyChangeInfo + + // An exchange that changed its key set, pending the user's + // confirmation. The wallet has already adopted the new key set, so + // the entry works and the older coins stay spendable; withdrawing is + // withheld until the change is confirmed. + interface ExchangeKeyChangeInfo { + // Master public key the exchange now uses, and the wallet now + // trusts. + currentMasterPub: string; + + currentCurrency: string; + + // Master public key the wallet's older funds were issued under. + supersededMasterPub: string; + + supersededCurrency: string; + + // Whether the new key set still advertises denominations the + // wallet holds coins of. False means the exchange does not offer + // to settle the older coins at all; true is only the exchange's + // claim, not proof of continuity. + sharesDenominations: boolean; + + firstSeen: TalerPreciseTimestamp; + } + +.. ts:def:: OperationErrorInfo + + interface OperationErrorInfo { + error: TalerErrorDetail; + } + +.. ts:def:: ExchangeTosStatus + + type ExchangeTosStatus = + | "pending" + | "proposed" + | "accepted" + | "missing-tos"; + +.. ts:def:: ExchangeEntrySource + + // How an exchange entry became known to the wallet. + type ExchangeEntrySource = + | "builtin" + | "user" + | "discovered" + | "unknown"; + +.. ts:def:: ExchangeUpdateStatus + + type ExchangeUpdateStatus = + | "initial" + | "initial-update" + | "suspended" + | "unavailable-update" + | "ready" + | "ready-update" + | "outdated-update"; + +.. ts:def:: GetExchangeEntryByUrlRequest + + interface GetExchangeEntryByUrlRequest { + exchangeBaseUrl: string; + } + +.. ts:def:: GetExchangeEntryByUrlResponse + + type GetExchangeEntryByUrlResponse = ExchangeListItem; diff --git a/core/wallet-core/exchanges/get-exchange-resources.rst b/core/wallet-core/exchanges/get-exchange-resources.rst @@ -0,0 +1,34 @@ +.. ts:op:: getExchangeResources + :read-only: + + Check whether the wallet still holds resources associated with an + exchange. + + **Request:** + + The request arguments must be a `GetExchangeResourcesRequest` + object. + + **Response:** + + On success, the result is a `GetExchangeResourcesResponse` object. + + **Details:** + + Resources are the coins and withdrawal groups (including internal + withdrawals of peer transactions) associated with the exchange; + ``hasResources`` is true if any of them exist. Clients can use + this to decide whether :ts:op:`deleteExchange` requires explicit + user confirmation. + +.. ts:def:: GetExchangeResourcesRequest + + interface GetExchangeResourcesRequest { + exchangeBaseUrl: string; + } + +.. ts:def:: GetExchangeResourcesResponse + + interface GetExchangeResourcesResponse { + hasResources: boolean; + } diff --git a/core/wallet-core/exchanges/get-exchange-tos.rst b/core/wallet-core/exchanges/get-exchange-tos.rst @@ -0,0 +1,70 @@ +.. ts:op:: getExchangeTos + + Get the current terms of service of an exchange. + + **Request:** + + The request arguments must be a `GetExchangeTosRequest` object. + + **Response:** + + On success, the result is a `GetExchangeTosResult` object. + + **Side effects:** + + Fetches the current terms of service from the exchange over the + network and stores the new ETag in the exchange record. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``. + + **Details:** + + The wallet downloads the terms of service from the exchange, using + ``acceptedFormat`` and ``acceptLanguage`` for content negotiation, + and updates its stored current ToS version tag. If the exchange + does not provide terms of service, ``tosStatus`` is + ``"missing-tos"`` and the content fields carry placeholder values. + +.. ts:def:: GetExchangeTosRequest + + interface GetExchangeTosRequest { + exchangeBaseUrl: string; + + // Acceptable content types for the ToS text, used for content + // negotiation with the exchange. + acceptedFormat?: string[]; + + // Preferred language for the ToS text. + acceptLanguage?: string; + + // Correlates progress notifications and allows cancellation. + progressToken?: string; + } + +.. ts:def:: GetExchangeTosResult + + interface GetExchangeTosResult { + // Markdown version of the current ToS. + content: string; + + // Version tag of the current ToS. + currentEtag: string; + + // Version tag of the last ToS that the user has accepted, if any. + acceptedEtag: string | undefined; + + // Accepted content type + contentType: string; + + // Language of the returned content. If missing, language is + // unknown. + contentLanguage: string | undefined; + + // Available languages as advertised by the exchange. + tosAvailableLanguages: string[]; + + tosStatus: ExchangeTosStatus; + } diff --git a/core/wallet-core/exchanges/list-exchanges.rst b/core/wallet-core/exchanges/list-exchanges.rst @@ -0,0 +1,45 @@ +.. ts:op:: listExchanges + :read-only: + + List exchanges known to the wallet. + + **Request:** + + The request arguments must be a `ListExchangesRequest` object. + Omitted filter fields do not restrict the result. + + **Response:** + + On success, the result is an `ExchangesListResponse` object. + +.. ts:def:: ListExchangesRequest + + interface ListExchangesRequest { + // Filter results to only include exchanges in the given scope. + filterByScope?: ScopeInfo; + + // Filter results to only include exchanges + // with the given status. + filterByExchangeEntryStatus?: ExchangeEntryStatus; + + // Filter results to only include exchanges with the + // given type. + filterByType?: ExchangeType; + } + +.. ts:def:: ExchangeType + + type ExchangeType = "demo" | "prod"; + +.. ts:def:: ExchangesListResponse + + interface ExchangesListResponse { + exchanges: ExchangeListItem[]; + } + +.. ts:def:: ExchangeEntryStatus + + type ExchangeEntryStatus = + | "preset" + | "ephemeral" + | "used"; diff --git a/core/wallet-core/exchanges/list-withdrawal-exchange-candidates.rst b/core/wallet-core/exchanges/list-withdrawal-exchange-candidates.rst @@ -0,0 +1,73 @@ +.. ts:op:: listWithdrawalExchangeCandidates + :read-only: + + List exchanges suitable for presentation in a withdrawal chooser. + + **Request:** + + The request arguments must be a + `ListWithdrawalExchangeCandidatesRequest` object. Omitted fields + use their documented defaults. + + **Response:** + + On success, the result is a `ListWithdrawalExchangeCandidatesResponse` + object. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + Candidates are taken from the wallet's exchange entries and from + the builtin exchange list; ephemeral entries and entries with + unknown currency are excluded. Demo and test exchanges are only + included when ``withDemo`` or ``withTest`` are set. Combining + ``presetOnly`` with ``withBuiltin: false`` is rejected with + ``WALLET_CORE_API_BAD_REQUEST``. Exchanges recently used for a + withdrawal are sorted first, and ``recommendationReasons`` states + why each candidate is suggested. + +.. ts:def:: ListWithdrawalExchangeCandidatesRequest + + type ListWithdrawalExchangeCandidatesRequest = + GetDefaultExchangesRequest; + +.. ts:def:: ListWithdrawalExchangeCandidatesResponse + + interface ListWithdrawalExchangeCandidatesResponse { + candidates: WithdrawalExchangeCandidate[]; + } + +.. ts:def:: WithdrawalExchangeCandidate + + interface WithdrawalExchangeCandidate { + // A taler://withdraw-exchange URI for the exchange. + talerUri: string; + + exchangeBaseUrl: string; + + currency: string; + + currencySpec: CurrencySpecification; + + exchangeEntryStatus: ExchangeEntryStatus; + + exchangeUpdateStatus: ExchangeUpdateStatus; + + source: ExchangeEntrySource; + + recommendationReasons: ExchangeRecommendationReason[]; + + lastWithdrawal?: TalerPreciseTimestamp; + } + +.. ts:def:: ExchangeRecommendationReason + + type ExchangeRecommendationReason = + | "preset" + | "user-added" + | "previous-withdrawal" + | "previously-used"; diff --git a/core/wallet-core/exchanges/purge-exchange-legacy-keys.rst b/core/wallet-core/exchanges/purge-exchange-legacy-keys.rst @@ -0,0 +1,44 @@ +.. ts:op:: purgeExchangeLegacyKeys + + Purge every non-current key set retained for an exchange URL. + + **Request:** + + The request arguments must be a `PurgeExchangeLegacyKeysRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes the retained non-current key sets of the exchange. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + Removes the coins, denominations and other stored data issued + under superseded master public keys of the exchange; withdrawals + of purged coins are marked as legacy. The ``currentMasterPub`` + must be the current master key observed by the client before + confirming the purge: the operation fails atomically with + ``WALLET_CORE_API_BAD_REQUEST`` if the exchange has changed keys + since, so a key rotation between review and execution cannot turn + the key the user just reviewed into another key that is silently + purged. + +.. ts:def:: PurgeExchangeLegacyKeysRequest + + interface PurgeExchangeLegacyKeysRequest { + exchangeBaseUrl: string; + + // Current master key observed by the client before confirming the + // purge. The operation fails atomically if the exchange has + // changed keys since. + currentMasterPub: string; + } diff --git a/core/wallet-core/exchanges/set-exchange-tos-accepted.rst b/core/wallet-core/exchanges/set-exchange-tos-accepted.rst @@ -0,0 +1,27 @@ +.. ts:op:: setExchangeTosAccepted + + Mark the current version of the exchange's terms of service as + accepted by the user. + + **Request:** + + The request arguments must be an `AcceptExchangeTosRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Records acceptance of the exchange's terms of service. + + **Details:** + + The accepted version is the current ToS version observed by the + wallet, as reported by :ts:op:`getExchangeTos`. + +.. ts:def:: AcceptExchangeTosRequest + + interface AcceptExchangeTosRequest { + exchangeBaseUrl: string; + } diff --git a/core/wallet-core/exchanges/set-exchange-tos-forgotten.rst b/core/wallet-core/exchanges/set-exchange-tos-forgotten.rst @@ -0,0 +1,17 @@ +.. ts:op:: setExchangeTosForgotten + + Forget the acceptance of the exchange's terms of service, marking + them as not accepted. + + **Request:** + + The request arguments must be an `AcceptExchangeTosRequest` object + (see :ts:op:`setExchangeTosAccepted`). + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Clears the recorded acceptance of the exchange's terms of service. diff --git a/core/wallet-core/exchanges/start-exchange-wallet-kyc.rst b/core/wallet-core/exchanges/start-exchange-wallet-kyc.rst @@ -0,0 +1,36 @@ +.. ts:op:: startExchangeWalletKyc + + Start the wallet KYC process at an exchange, requesting + authorization to hold funds up to the given amount threshold. + + **Request:** + + The request arguments must be a `StartExchangeWalletKycRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Starts the wallet KYC process at the exchange over the network; + progress is reported via transactions and notifications. + + **Details:** + + The request only initiates the KYC process; the wallet then drives + it in the background and reports status changes via notifications. + If a KYC threshold of at least ``amount`` was already granted or + is already being requested, the operation does nothing. Once + started, the exchange entry is marked as used, as the wallet keeps + an account at the exchange. + +.. ts:def:: StartExchangeWalletKycRequest + + interface StartExchangeWalletKycRequest { + exchangeBaseUrl: string; + + // Amount threshold that the KYC process should authorize. + amount: AmountString; + } diff --git a/core/wallet-core/exchanges/update-exchange-entry.rst b/core/wallet-core/exchanges/update-exchange-entry.rst @@ -0,0 +1,45 @@ +.. ts:op:: updateExchangeEntry + + Update an exchange entry. + + Only starts updating the exchange entry. After this request + finishes, it is not guaranteed that the exchange entry has been + updated. Use notifications and the :ts:op:`listExchanges` request + to check the status. + + **Request:** + + The request arguments must be an `UpdateExchangeEntryRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Starts an update of the exchange entry: data is fetched over the + network and notifications are emitted; completion is reported + asynchronously. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, + ``WALLET_EXCHANGE_ENTRY_UPDATE_CONFLICT``, + ``WALLET_EXCHANGE_UNAVAILABLE``. + + **Details:** + + Setting ``force`` starts the update even when the entry was + recently updated or the exchange is currently marked unavailable. + +.. ts:def:: UpdateExchangeEntryRequest + + interface UpdateExchangeEntryRequest { + exchangeBaseUrl: string; + + // Force the update even if the entry was recently updated or + // the exchange is currently marked unavailable. + force?: boolean; + } diff --git a/core/wallet-core/global-currency.rst b/core/wallet-core/global-currency.rst @@ -1,292 +0,0 @@ -.. _wallet-op-listGlobalCurrencyExchanges: - -**listGlobalCurrencyExchanges** - -List the exchanges registered for global currencies. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `ListGlobalCurrencyExchangesResponse` - object. - -.. ts:def:: ListGlobalCurrencyExchangesResponse - - interface ListGlobalCurrencyExchangesResponse { - exchanges: { - currency: string; - exchangeBaseUrl: string; - exchangeMasterPub: string; - }[]; - } - - -.. _wallet-op-listGlobalCurrencyAuditors: - -**listGlobalCurrencyAuditors** - -List the auditors the wallet trusts for global currencies. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `ListGlobalCurrencyAuditorsResponse` - object. - -.. ts:def:: ListGlobalCurrencyAuditorsResponse - - interface ListGlobalCurrencyAuditorsResponse { - auditors: { - currency: string; - auditorBaseUrl: string; - auditorPub: string; - }[]; - } - - -.. _wallet-op-addGlobalCurrencyExchange: - -**addGlobalCurrencyExchange** - -Register an exchange for a global currency. - -**Request:** - - The request must be an `AddGlobalCurrencyExchangeRequest` object. - -**Response:** - - On success, the result is an empty object (`EmptyObject`). - -**Details:** - - Adding an exchange that is already registered is a no-op. - Currency information already known under the exchange's own scope - is copied to the global scope. The ``exchangeBaseUrl`` must be a - canonicalized base URL (see :ref:`canonicalizeBaseUrl - <wallet-op-canonicalizeBaseUrl>`). When the configuration - changed, a ``balance-change`` notification is emitted. - -.. ts:def:: AddGlobalCurrencyExchangeRequest - - interface AddGlobalCurrencyExchangeRequest { - currency: string; - exchangeBaseUrl: string; - exchangeMasterPub: string; - } - - -.. _wallet-op-removeGlobalCurrencyExchange: - -**removeGlobalCurrencyExchange** - -Remove an exchange registered for a global currency. - -**Request:** - - The request must be a `RemoveGlobalCurrencyExchangeRequest` - object. - -**Response:** - - On success, the result is an empty object (`EmptyObject`). - -**Details:** - - Removing an exchange that is not registered is a no-op. When the - last exchange for a currency is removed, the currency information - for the global scope is removed as well. When the configuration - changed, a ``balance-change`` notification is emitted. - -.. ts:def:: RemoveGlobalCurrencyExchangeRequest - - interface RemoveGlobalCurrencyExchangeRequest { - currency: string; - exchangeBaseUrl: string; - exchangeMasterPub: string; - } - - -.. _wallet-op-addGlobalCurrencyAuditor: - -**addGlobalCurrencyAuditor** - -Register a trusted auditor for a global currency. - -**Request:** - - The request must be an `AddGlobalCurrencyAuditorRequest` object. - -**Response:** - - On success, the result is an empty object (`EmptyObject`). - -**Details:** - - Adding an auditor that is already registered is a no-op. The - ``auditorBaseUrl`` must be a canonicalized base URL (see - :ref:`canonicalizeBaseUrl <wallet-op-canonicalizeBaseUrl>`). When - the configuration changed, a ``balance-change`` notification is - emitted. - -.. ts:def:: AddGlobalCurrencyAuditorRequest - - interface AddGlobalCurrencyAuditorRequest { - currency: string; - auditorBaseUrl: string; - auditorPub: string; - } - - -.. _wallet-op-removeGlobalCurrencyAuditor: - -**removeGlobalCurrencyAuditor** - -Remove a trusted auditor for a global currency. - -**Request:** - - The request must be a `RemoveGlobalCurrencyAuditorRequest` object. - -**Response:** - - On success, the result is an empty object (`EmptyObject`). - -**Details:** - - Removing an auditor that is not registered is a no-op. When the - configuration changed, a ``balance-change`` notification is - emitted. - -.. ts:def:: RemoveGlobalCurrencyAuditorRequest - - interface RemoveGlobalCurrencyAuditorRequest { - currency: string; - auditorBaseUrl: string; - auditorPub: string; - } - - -.. _wallet-op-getCurrencySpecification: - -**getCurrencySpecification** - -Get the currency specification (input and rendering rules) for a -currency scope. - -**Request:** - - The request must be a `GetCurrencySpecificationRequest` object. - -**Response:** - - On success, the result is a `GetCurrencySpecificationResponse` - object. - -**Details:** - - The specification recorded for the given scope is returned. For - the demonstration currencies ``KUDOS`` and ``TESTKUDOS``, a - hard-coded specification is returned. When no specification is - known for the scope, a default with two fractional digits derived - from the currency name is returned. - -.. ts:def:: GetCurrencySpecificationRequest - - interface GetCurrencySpecificationRequest { - scope: ScopeInfo; - } - -.. ts:def:: GetCurrencySpecificationResponse - - interface GetCurrencySpecificationResponse { - currencySpecification: CurrencySpecification; - } - -.. ts:def:: ScopeInfo - - // Currency scope, discriminated on the "type" field. - type ScopeInfo = - | ScopeInfoGlobal - | ScopeInfoExchange - | ScopeInfoAuditor - | ScopeInfoExchangeLegacyKeys; - -.. ts:def:: ScopeInfoGlobal - - type ScopeInfoGlobal = { - type: ScopeType.Global; - currency: string; - }; - -.. ts:def:: ScopeInfoExchange - - type ScopeInfoExchange = { - type: ScopeType.Exchange; - currency: string; - url: string; - }; - -.. ts:def:: ScopeInfoAuditor - - type ScopeInfoAuditor = { - type: ScopeType.Auditor; - currency: string; - url: string; - }; - -.. ts:def:: ScopeInfoExchangeLegacyKeys - - type ScopeInfoExchangeLegacyKeys = { - type: ScopeType.ExchangeLegacyKeys; - currency: string; - url: string; - - // The superseded key the funds were issued under. - masterPub: string; - }; - -.. ts:def:: ScopeType - - enum ScopeType { - Global = "global", - Exchange = "exchange", - Auditor = "auditor", - - // Funds issued under a master public key the exchange has - // since replaced. - ExchangeLegacyKeys = "exchange-legacy-keys", - } - -.. ts:def:: CurrencySpecification - - // Input and rendering rules for a currency (see DD51). - interface CurrencySpecification { - // Name of the currency. - name: string; - - // How many digits the user may enter after the decimal - // separator. - num_fractional_input_digits: Integer; - - // Number of fractional digits to render in normal font and - // size. - num_fractional_normal_digits: Integer; - - // Number of fractional digits to render always, padding with - // zeros if needed. - num_fractional_trailing_zero_digits: Integer; - - // Map of powers of 10 to alternative currency names / symbols; - // always has an entry under "0" with the base name, e.g. - // "0 => €" or "3 => k€". - alt_unit_names: { [log10: string]: string }; - - common_amounts?: AmountString[]; - } diff --git a/core/wallet-core/global-currency/add-global-currency-auditor.rst b/core/wallet-core/global-currency/add-global-currency-auditor.rst @@ -0,0 +1,31 @@ +.. ts:op:: addGlobalCurrencyAuditor + + Register a trusted auditor for a global currency. + + **Request:** + + The request must be an `AddGlobalCurrencyAuditorRequest` object. + + **Response:** + + On success, the result is an empty object (`EmptyObject`). + + **Side effects:** + + Registers the auditor for the global currency in the wallet's + database. When the configuration changed, a ``balance-change`` + notification is emitted. + + **Details:** + + Adding an auditor that is already registered is a no-op. The + ``auditorBaseUrl`` must be a canonicalized base URL (see + :ts:op:`canonicalizeBaseUrl`). + +.. ts:def:: AddGlobalCurrencyAuditorRequest + + interface AddGlobalCurrencyAuditorRequest { + currency: string; + auditorBaseUrl: string; + auditorPub: string; + } diff --git a/core/wallet-core/global-currency/add-global-currency-exchange.rst b/core/wallet-core/global-currency/add-global-currency-exchange.rst @@ -0,0 +1,34 @@ +.. ts:op:: addGlobalCurrencyExchange + + Register an exchange for a global currency. + + **Request:** + + The request must be an `AddGlobalCurrencyExchangeRequest` + object. + + **Response:** + + On success, the result is an empty object (`EmptyObject`). + + **Side effects:** + + Registers the exchange for the global currency in the wallet's + database. When the configuration changed, a ``balance-change`` + notification is emitted. + + **Details:** + + Adding an exchange that is already registered is a no-op. + Currency information already known under the exchange's own + scope is copied to the global scope. The ``exchangeBaseUrl`` + must be a canonicalized base URL (see + :ts:op:`canonicalizeBaseUrl`). + +.. ts:def:: AddGlobalCurrencyExchangeRequest + + interface AddGlobalCurrencyExchangeRequest { + currency: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; + } diff --git a/core/wallet-core/global-currency/get-currency-specification.rst b/core/wallet-core/global-currency/get-currency-specification.rst @@ -0,0 +1,116 @@ +.. ts:op:: getCurrencySpecification + :read-only: + + Get the currency specification (input and rendering rules) for a + currency scope. + + **Request:** + + The request must be a `GetCurrencySpecificationRequest` object. + + **Response:** + + On success, the result is a `GetCurrencySpecificationResponse` + object. + + **Details:** + + The specification recorded for the given scope is returned. For + the demonstration currencies ``KUDOS`` and ``TESTKUDOS``, a + hard-coded specification is returned. When no specification is + known for the scope, a default with two fractional digits + derived from the currency name is returned. + +.. ts:def:: GetCurrencySpecificationRequest + + interface GetCurrencySpecificationRequest { + scope: ScopeInfo; + } + +.. ts:def:: GetCurrencySpecificationResponse + + interface GetCurrencySpecificationResponse { + currencySpecification: CurrencySpecification; + } + +.. ts:def:: ScopeInfo + + // Currency scope, discriminated on the "type" field. + type ScopeInfo = + | ScopeInfoGlobal + | ScopeInfoExchange + | ScopeInfoAuditor + | ScopeInfoExchangeLegacyKeys; + +.. ts:def:: ScopeInfoGlobal + + type ScopeInfoGlobal = { + type: ScopeType.Global; + currency: string; + }; + +.. ts:def:: ScopeInfoExchange + + type ScopeInfoExchange = { + type: ScopeType.Exchange; + currency: string; + url: string; + }; + +.. ts:def:: ScopeInfoAuditor + + type ScopeInfoAuditor = { + type: ScopeType.Auditor; + currency: string; + url: string; + }; + +.. ts:def:: ScopeInfoExchangeLegacyKeys + + type ScopeInfoExchangeLegacyKeys = { + type: ScopeType.ExchangeLegacyKeys; + currency: string; + url: string; + + // The superseded key the funds were issued under. + masterPub: string; + }; + +.. ts:def:: ScopeType + + enum ScopeType { + Global = "global", + Exchange = "exchange", + Auditor = "auditor", + + // Funds issued under a master public key the exchange has + // since replaced. + ExchangeLegacyKeys = "exchange-legacy-keys", + } + +.. ts:def:: CurrencySpecification + + // Input and rendering rules for a currency (see DD51). + interface CurrencySpecification { + // Name of the currency. + name: string; + + // How many digits the user may enter after the decimal + // separator. + num_fractional_input_digits: Integer; + + // Number of fractional digits to render in normal font and + // size. + num_fractional_normal_digits: Integer; + + // Number of fractional digits to render always, padding with + // zeros if needed. + num_fractional_trailing_zero_digits: Integer; + + // Map of powers of 10 to alternative currency names / symbols; + // always has an entry under "0" with the base name, e.g. + // "0 => €" or "3 => k€". + alt_unit_names: { [log10: string]: string }; + + common_amounts?: AmountString[]; + } diff --git a/core/wallet-core/global-currency/list-global-currency-auditors.rst b/core/wallet-core/global-currency/list-global-currency-auditors.rst @@ -0,0 +1,23 @@ +.. ts:op:: listGlobalCurrencyAuditors + :read-only: + + List the auditors the wallet trusts for global currencies. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `ListGlobalCurrencyAuditorsResponse` + object. + +.. ts:def:: ListGlobalCurrencyAuditorsResponse + + interface ListGlobalCurrencyAuditorsResponse { + auditors: { + currency: string; + auditorBaseUrl: string; + auditorPub: string; + }[]; + } diff --git a/core/wallet-core/global-currency/list-global-currency-exchanges.rst b/core/wallet-core/global-currency/list-global-currency-exchanges.rst @@ -0,0 +1,23 @@ +.. ts:op:: listGlobalCurrencyExchanges + :read-only: + + List the exchanges registered for global currencies. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a + `ListGlobalCurrencyExchangesResponse` object. + +.. ts:def:: ListGlobalCurrencyExchangesResponse + + interface ListGlobalCurrencyExchangesResponse { + exchanges: { + currency: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; + }[]; + } diff --git a/core/wallet-core/global-currency/remove-global-currency-auditor.rst b/core/wallet-core/global-currency/remove-global-currency-auditor.rst @@ -0,0 +1,30 @@ +.. ts:op:: removeGlobalCurrencyAuditor + + Remove a trusted auditor for a global currency. + + **Request:** + + The request must be a `RemoveGlobalCurrencyAuditorRequest` + object. + + **Response:** + + On success, the result is an empty object (`EmptyObject`). + + **Side effects:** + + Removes the registration from the wallet's database. When the + configuration changed, a ``balance-change`` notification is + emitted. + + **Details:** + + Removing an auditor that is not registered is a no-op. + +.. ts:def:: RemoveGlobalCurrencyAuditorRequest + + interface RemoveGlobalCurrencyAuditorRequest { + currency: string; + auditorBaseUrl: string; + auditorPub: string; + } diff --git a/core/wallet-core/global-currency/remove-global-currency-exchange.rst b/core/wallet-core/global-currency/remove-global-currency-exchange.rst @@ -0,0 +1,31 @@ +.. ts:op:: removeGlobalCurrencyExchange + + Remove an exchange registered for a global currency. + + **Request:** + + The request must be a `RemoveGlobalCurrencyExchangeRequest` + object. + + **Response:** + + On success, the result is an empty object (`EmptyObject`). + + **Side effects:** + + Removes the registration from the wallet's database; removing + the last exchange of a currency also removes the global + currency information. When the configuration changed, a + ``balance-change`` notification is emitted. + + **Details:** + + Removing an exchange that is not registered is a no-op. + +.. ts:def:: RemoveGlobalCurrencyExchangeRequest + + interface RemoveGlobalCurrencyExchangeRequest { + currency: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; + } diff --git a/core/wallet-core/hints.rst b/core/wallet-core/hints.rst @@ -1,130 +0,0 @@ -.. _wallet-op-hintNetworkAvailability: - -**hintNetworkAvailability** - -Inform wallet-core about the host system's network connectivity. - -**Request:** - - The request ``args`` must be a `HintNetworkAvailabilityRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - When the reported availability changes, wallet-core restarts all - running background tasks: tasks that are blocked waiting for the - network resume when connectivity is back, and tasks notice the outage - and wait when it goes away. Reporting the already known state has no - effect. - -.. ts:def:: HintNetworkAvailabilityRequest - - interface HintNetworkAvailabilityRequest { - // Whether the host system currently has network connectivity. - isNetworkAvailable: boolean; - } - - -.. _wallet-op-hintPowerState: - -**hintPowerState** - -Report the host system's current power source. - -**Request:** - - The request ``args`` must be a `HintPowerStateRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The power source controls whether wallet-core may refresh coins - opportunistically, long before they expire: such early refreshes are - only made when they are free of charge, and only on ``external`` - power. The ``unknown`` power source never qualifies. When the power - source changes, wallet-core restarts all running background tasks. - -.. ts:def:: HintPowerStateRequest - - interface HintPowerStateRequest { - // Currently observed power source of the host system. - powerSource: WalletPowerSource; - } - -.. ts:def:: WalletPowerSource - - // Power source of the host system, as observed by the client. - type WalletPowerSource = "external" | "battery" | "unknown"; - - -.. _wallet-op-dismissWalletWarning: - -**dismissWalletWarning** - -Dismiss a completed renewal notice. Active expiration risks cannot be -dismissed. - -**Request:** - - The request ``args`` must be a `DismissWalletWarningRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - When wallet-core automatically refreshed coins that were close to - expiration, the renewal is reported as a `CashRenewalNotice` in the - ``refreshInfo.recoveries`` of the affected `WalletBalance`. Passing - the notice's ``warningId`` to this operation dismisses the notice; a - ``balance-change`` notification is emitted, and the notice is no - longer reported. Renewal notices of refresh operations that are still - active, as well as unknown warning identifiers, are ignored; the - operation still succeeds. - -.. ts:def:: DismissWalletWarningRequest - - interface DismissWalletWarningRequest { - // Identifier of the wallet warning to dismiss, as found in the - // warningId field of a CashRenewalNotice. - warningId: string; - } - - -.. _wallet-op-hintApplicationResumed: - -**hintApplicationResumed** - -Give wallet-core a kick and restart all pending tasks. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `HintApplicationResumedResponse` object. - -**Details:** - - Useful when the host application was suspended and resumed, as active - network requests might have stalled. Wallet-core restarts its - background tasks and performs a database write and read health check; - the outcome of the checks is reported in the response. - -.. ts:def:: HintApplicationResumedResponse - - interface HintApplicationResumedResponse { - // Did the database write health check succeed? - dbWriteHealthy: boolean; - - // Did the database read health check succeed? - dbReadHealthy: boolean; - } diff --git a/core/wallet-core/hints/dismiss-wallet-warning.rst b/core/wallet-core/hints/dismiss-wallet-warning.rst @@ -0,0 +1,35 @@ +.. ts:op:: dismissWalletWarning + + Dismiss a completed renewal notice. Active expiration risks cannot be + dismissed. + + **Request:** + + The request ``args`` must be a `DismissWalletWarningRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Persistently marks a completed renewal notice as dismissed; a + ``balance-change`` notification is emitted. + + **Details:** + + When wallet-core automatically refreshed coins that were close to + expiration, the renewal is reported as a `CashRenewalNotice` in the + ``refreshInfo.recoveries`` of the affected `WalletBalance`. Passing + the notice's ``warningId`` to this operation dismisses the notice, so + that it is no longer reported. Renewal notices of refresh operations + that are still active, as well as unknown warning identifiers, are + ignored; the operation still succeeds. + +.. ts:def:: DismissWalletWarningRequest + + interface DismissWalletWarningRequest { + // Identifier of the wallet warning to dismiss, as found in the + // warningId field of a CashRenewalNotice. + warningId: string; + } diff --git a/core/wallet-core/hints/hint-application-resumed.rst b/core/wallet-core/hints/hint-application-resumed.rst @@ -0,0 +1,32 @@ +.. ts:op:: hintApplicationResumed + + Give wallet-core a kick and restart all pending tasks. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `HintApplicationResumedResponse` object. + + **Side effects:** + + Restarts all pending tasks and stalled network requests. + + **Details:** + + Useful when the host application was suspended and resumed, as active + network requests might have stalled. Wallet-core restarts its + background tasks and performs a database write and read health check; + the outcome of the checks is reported in the response. + +.. ts:def:: HintApplicationResumedResponse + + interface HintApplicationResumedResponse { + // Did the database write health check succeed? + dbWriteHealthy: boolean; + + // Did the database read health check succeed? + dbReadHealthy: boolean; + } diff --git a/core/wallet-core/hints/hint-network-availability.rst b/core/wallet-core/hints/hint-network-availability.rst @@ -0,0 +1,31 @@ +.. ts:op:: hintNetworkAvailability + + Inform wallet-core about the host system's network connectivity. + + **Request:** + + The request ``args`` must be a `HintNetworkAvailabilityRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Records the host's network state; on change, background tasks are + woken up and may start network activity. + + **Details:** + + When the reported availability changes, wallet-core restarts all + running background tasks: tasks that are blocked waiting for the + network resume when connectivity is back, and tasks notice the outage + and wait when it goes away. Reporting the already known state has no + effect. + +.. ts:def:: HintNetworkAvailabilityRequest + + interface HintNetworkAvailabilityRequest { + // Whether the host system currently has network connectivity. + isNetworkAvailable: boolean; + } diff --git a/core/wallet-core/hints/hint-power-state.rst b/core/wallet-core/hints/hint-power-state.rst @@ -0,0 +1,37 @@ +.. ts:op:: hintPowerState + + Report the host system's current power source. + + **Request:** + + The request ``args`` must be a `HintPowerStateRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Records the host's power state, which affects whether free + opportunistic refreshes run. When the power source changes, running + background tasks are woken up. + + **Details:** + + The power source controls whether wallet-core may refresh coins + opportunistically, long before they expire: such early refreshes are + only made when they are free of charge, and only on ``external`` + power. The ``unknown`` power source never qualifies. When the power + source changes, wallet-core restarts all running background tasks. + +.. ts:def:: HintPowerStateRequest + + interface HintPowerStateRequest { + // Currently observed power source of the host system. + powerSource: WalletPowerSource; + } + +.. ts:def:: WalletPowerSource + + // Power source of the host system, as observed by the client. + type WalletPowerSource = "external" | "battery" | "unknown"; diff --git a/core/wallet-core/init.rst b/core/wallet-core/init.rst @@ -1,196 +0,0 @@ -.. _wallet-op-initWallet: - -**initWallet** - -Initialize wallet-core. This must be the first request made to -wallet-core; every other operation fails until initialization has -completed. - -**Request:** - - The request must be an `InitRequest` object. - -**Response:** - - On success, the result is an `InitResponse` object. - -**Details:** - - Initialization opens the wallet database, runs internal data - migrations and installs the built-in default exchanges (unless - ``config.testing.skipDefaults`` is set). When - ``config.features.migrateNativeDb`` is set, the database is first - migrated to wallet-core's native sqlite schema. Unless - ``config.lazyTaskLoop`` is set, the background task loop is started - and begins processing pending transactions. - - Calling ``initWallet`` again after a successful initialization - re-initializes the wallet with the new configuration, exactly like - :ref:`setWalletRunConfig <wallet-op-setWalletRunConfig>`. - -.. ts:def:: InitRequest - - interface InitRequest { - // Configuration overrides; omitted fields fall back to defaults. - config?: PartialWalletRunConfig; - } - -.. ts:def:: PartialWalletRunConfig - - interface PartialWalletRunConfig { - testing?: Partial<WalletRunConfig["testing"]>; - features?: Partial<WalletRunConfig["features"]>; - lazyTaskLoop?: Partial<WalletRunConfig["lazyTaskLoop"]>; - logLevel?: Partial<WalletRunConfig["logLevel"]>; - } - -.. ts:def:: WalletRunConfig - - interface WalletRunConfig { - // Unsafe options which should only be used to create - // testing environments. - testing: { - devModeActive: boolean; - insecureTrustExchange: boolean; - preventThrottling: boolean; - skipDefaults: boolean; - emitObservabilityEvents?: boolean; - - // Coin selection algorithm to use when spending. - // Defaults to the TALER_WALLET_COINSEL environment variable, - // and to "default" when that is unset. - coinSelectionAlgorithm: CoinSelectionAlgorithm; - }; - - // Configuration values that may be safe to show to the user. - features: { - allowHttp: boolean; - - // Migrate the wallet database to wallet-core's native sqlite - // schema, replacing the IndexedDB emulation. Checked on every - // initialization; off by default. - migrateNativeDb: boolean; - - // Use the native sqlite schema when initializing a new, empty - // database; never converts an existing IndexedDB wallet. - useNativeDb: boolean; - }; - - // Start processing tasks only when explicitly required, even - // after init has been called. - lazyTaskLoop: boolean; - - // Global log level. - logLevel: string; - } - -.. ts:def:: CoinSelectionAlgorithm - - // Coin selection algorithm the wallet uses when spending. - // "legacy-2024" is the algorithm shipped in 2024, kept for external - // test suites that pin the coin selections it produces. - type CoinSelectionAlgorithm = "default" | "legacy-2024"; - -.. ts:def:: InitResponse - - interface InitResponse { - // Version information about the initialized wallet-core. - versionInfo: WalletCoreVersion; - - // Database backend used by the initialized wallet. - databaseBackend: WalletDatabaseBackend; - } - -.. ts:def:: WalletDatabaseBackend - - // Database backends that wallet-core can run on. - type WalletDatabaseBackend = "indexeddb" | "sqlite"; - - -.. _wallet-op-setWalletRunConfig: - -**setWalletRunConfig** - -Change the configuration of wallet-core. - -**Request:** - - The request must be an `InitRequest` object. - -**Response:** - - On success, the result is an `InitResponse` object. - -**Details:** - - This operation is currently an alias for :ref:`initWallet - <wallet-op-initWallet>`: both operations run the same initialization, - so ``setWalletRunConfig`` can also serve as the first request that - initializes the wallet. Fields not present in ``config`` are reset - to their defaults. - - -.. _wallet-op-getVersion: - -**getVersion** - -Get version information about wallet-core and the protocol versions -it supports. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `WalletCoreVersion` object. - -.. ts:def:: WalletCoreVersion - - interface WalletCoreVersion { - implementationSemver: string; - implementationGitHash: string; - - // Wallet-core protocol version supported by this implementation - // of the API ("server" version). - version: string; - exchange: string; - merchant: string; - - bankIntegrationApiRange: string; - bankConversionApiRange: string; - corebankApiRange: string; - - // Deprecated: the bank API was split into multiple APIs with - // separate versioning. - bank: string; - - // Deprecated. - hash: string | undefined; - - // Deprecated, will be removed. - devMode: boolean; - } - - -.. _wallet-op-shutdown: - -**shutdown** - -Shut down wallet-core: stop the background task loop, timers and -cryptographic workers, and close the wallet database. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - -**Details:** - - After a shutdown, every operation other than ``shutdown`` itself - fails with ``WALLET_CORE_NOT_AVAILABLE``. To use the wallet again, - wallet-core must be restarted and :ref:`initWallet - <wallet-op-initWallet>` called again. diff --git a/core/wallet-core/init/get-version.rst b/core/wallet-core/init/get-version.rst @@ -0,0 +1,40 @@ +.. ts:op:: getVersion + :read-only: + + Get version information about wallet-core and the protocol versions + it supports. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `WalletCoreVersion` object. + +.. ts:def:: WalletCoreVersion + + interface WalletCoreVersion { + implementationSemver: string; + implementationGitHash: string; + + // Wallet-core protocol version supported by this implementation + // of the API ("server" version). + version: string; + exchange: string; + merchant: string; + + bankIntegrationApiRange: string; + bankConversionApiRange: string; + corebankApiRange: string; + + // Deprecated: the bank API was split into multiple APIs with + // separate versioning. + bank: string; + + // Deprecated. + hash: string | undefined; + + // Deprecated, will be removed. + devMode: boolean; + } diff --git a/core/wallet-core/init/init-wallet.rst b/core/wallet-core/init/init-wallet.rst @@ -0,0 +1,105 @@ +.. ts:op:: initWallet + + Initialize wallet-core. This must be the first request made to + wallet-core; every other operation fails until initialization has + completed. + + **Request:** + + The request must be an `InitRequest` object. + + **Response:** + + On success, the result is an `InitResponse` object. + + **Side effects:** + + Initializes the wallet: opens the wallet database (migrating it + to the native sqlite schema when ``config.features.migrateNativeDb`` + is set), installs the built-in default exchanges (unless + ``config.testing.skipDefaults`` is set) and starts the background + task loop (unless ``config.lazyTaskLoop`` is set). + + **Details:** + + Calling ``initWallet`` again after a successful initialization + re-initializes the wallet with the new configuration, exactly + like :ts:op:`setWalletRunConfig`. + +.. ts:def:: InitRequest + + interface InitRequest { + // Configuration overrides; omitted fields fall back to defaults. + config?: PartialWalletRunConfig; + } + +.. ts:def:: PartialWalletRunConfig + + interface PartialWalletRunConfig { + testing?: Partial<WalletRunConfig["testing"]>; + features?: Partial<WalletRunConfig["features"]>; + lazyTaskLoop?: Partial<WalletRunConfig["lazyTaskLoop"]>; + logLevel?: Partial<WalletRunConfig["logLevel"]>; + } + +.. ts:def:: WalletRunConfig + + interface WalletRunConfig { + // Unsafe options which should only be used to create + // testing environments. + testing: { + devModeActive: boolean; + insecureTrustExchange: boolean; + preventThrottling: boolean; + skipDefaults: boolean; + emitObservabilityEvents?: boolean; + + // Coin selection algorithm to use when spending. + // Defaults to the TALER_WALLET_COINSEL environment variable, + // and to "default" when that is unset. + coinSelectionAlgorithm: CoinSelectionAlgorithm; + }; + + // Configuration values that may be safe to show to the user. + features: { + allowHttp: boolean; + + // Migrate the wallet database to wallet-core's native sqlite + // schema, replacing the IndexedDB emulation. Checked on every + // initialization; off by default. + migrateNativeDb: boolean; + + // Use the native sqlite schema when initializing a new, empty + // database; never converts an existing IndexedDB wallet. + useNativeDb: boolean; + }; + + // Start processing tasks only when explicitly required, even + // after init has been called. + lazyTaskLoop: boolean; + + // Global log level. + logLevel: string; + } + +.. ts:def:: CoinSelectionAlgorithm + + // Coin selection algorithm the wallet uses when spending. + // "legacy-2024" is the algorithm shipped in 2024, kept for external + // test suites that pin the coin selections it produces. + type CoinSelectionAlgorithm = "default" | "legacy-2024"; + +.. ts:def:: InitResponse + + interface InitResponse { + // Version information about the initialized wallet-core. + versionInfo: WalletCoreVersion; + + // Database backend used by the initialized wallet. + databaseBackend: WalletDatabaseBackend; + } + +.. ts:def:: WalletDatabaseBackend + + // Database backends that wallet-core can run on. + type WalletDatabaseBackend = "indexeddb" | "sqlite"; diff --git a/core/wallet-core/init/set-wallet-run-config.rst b/core/wallet-core/init/set-wallet-run-config.rst @@ -0,0 +1,24 @@ +.. ts:op:: setWalletRunConfig + + Change the configuration of wallet-core. + + **Request:** + + The request must be an `InitRequest` object. + + **Response:** + + On success, the result is an `InitResponse` object. + + **Side effects:** + + Re-initializes the wallet with the new configuration, with the + same effects as :ts:op:`initWallet`. + + **Details:** + + This operation is currently an alias for :ts:op:`initWallet`: + both operations run the same initialization, so + ``setWalletRunConfig`` can also serve as the first request that + initializes the wallet. Fields not present in ``config`` are + reset to their defaults. diff --git a/core/wallet-core/init/shutdown.rst b/core/wallet-core/init/shutdown.rst @@ -0,0 +1,23 @@ +.. ts:op:: shutdown + + Shut down wallet-core. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Stops the background task loop, timers and cryptographic workers + and closes the wallet database. Afterwards, every operation + other than ``shutdown`` itself fails with + ``WALLET_CORE_NOT_AVAILABLE``. + + **Details:** + + To use the wallet again, wallet-core must be restarted and + :ts:op:`initWallet` called again. diff --git a/core/wallet-core/mailbox.rst b/core/wallet-core/mailbox.rst @@ -1,260 +0,0 @@ -.. _wallet-op-getMailbox: - -**getMailbox** - -Get the mailbox configuration stored locally for a mailbox service. -This operation only reads the wallet's local database. - -**Request:** - - The request arguments are a `MailboxBaseUrl` object. - -**Response:** - - On success, the result is a `GetMailboxResponse` object. The - ``mailboxConfiguration`` field is absent if the wallet has no - mailbox configuration for the given base URL. - -.. ts:def:: MailboxBaseUrl - - interface MailboxBaseUrl { - // Base URL of the mailbox service. - mailboxBaseUrl: string; - } - -.. ts:def:: GetMailboxResponse - - interface GetMailboxResponse { - // Locally stored configuration for the mailbox service, if any. - mailboxConfiguration?: MailboxConfiguration; - } - -.. ts:def:: MailboxConfiguration - - interface MailboxConfiguration { - // Base URL of the mailbox service. - mailboxBaseUrl: string; - - // Private EdDSA signing key of the mailbox. - privateKey: EddsaPrivateKeyString; - - // Private HPKE encryption key of the mailbox. - privateEncryptionKey: string; - - // Expiration of the current mailbox registration. - expiration: Timestamp; - - // Mailbox address (hash of the public signing key). - hAddress: string; - - // Set when the mailbox service requires payment to complete - // the registration. - payUri?: TalerUri; - } - - -.. _wallet-op-initializeMailbox: - -**initializeMailbox** - -Create a new mailbox at a mailbox service: wallet-core generates fresh -signing and encryption keys, registers the mailbox with the service and -stores the resulting configuration locally. - -**Request:** - - The request arguments are a `MailboxBaseUrl` object. - -**Response:** - - On success, the result is the newly created `MailboxConfiguration` - object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_MAILBOX_UNAVAILABLE``, ``GENERIC_FORBIDDEN``. - -**Details:** - - A fresh key pair is generated on every call, so each call creates a - new mailbox identity at the service. The registration is made with - an expiration one year in the future. If the mailbox service - requires payment for the registration, the operation still succeeds - and the returned configuration has ``payUri`` set, which the client - can use to pay for the registration. ``GENERIC_FORBIDDEN`` indicates - that the mailbox service refused the registration. - - -.. _wallet-op-getMailboxMessage: - -**getMailboxMessage** - -Get the mailbox messages stored locally in the wallet. - -Note that the TypeScript client library exposes this operation as -``GetMailboxMessages`` (with a trailing "s"), while the operation name -sent on the wire is ``getMailboxMessage``. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `MailboxMessagesResponse` object. - -**Details:** - - This operation only reads the wallet's local database; use - :ref:`refreshMailbox <wallet-op-refreshMailbox>` to download new - messages from the mailbox service. - -.. ts:def:: MailboxMessagesResponse - - interface MailboxMessagesResponse { - // Locally stored mailbox messages. - messages: MailboxMessageRecord[]; - } - -.. ts:def:: MailboxMessageRecord - - // Record metadata for mailbox messages. - interface MailboxMessageRecord { - // Origin mailbox. - originMailboxBaseUrl: string; - - // Time of download. - downloadedAt: Timestamp; - - // Taler URI in message. - talerUri: string; - } - - -.. _wallet-op-addMailboxMessage: - -**addMailboxMessage** - -Store a mailbox message in the wallet's local database. If a message -with the same ``originMailboxBaseUrl`` and ``talerUri`` already exists, -its ``downloadedAt`` timestamp is updated. Afterwards, wallet-core -emits a ``mailbox-message-added`` notification. - -**Request:** - - The request arguments are an `AddMailboxMessageRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: AddMailboxMessageRequest - - interface AddMailboxMessageRequest { - // The message to store. - message: MailboxMessageRecord; - } - - -.. _wallet-op-deleteMailboxMessage: - -**deleteMailboxMessage** - -Delete a mailbox message from the wallet's local database. Afterwards, -wallet-core emits a ``mailbox-message-deleted`` notification. - -**Request:** - - The request arguments are a `DeleteMailboxMessageRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The message is identified by the ``originMailboxBaseUrl`` and - ``talerUri`` fields of the given message; the ``downloadedAt`` field - is ignored. Deleting a message that does not exist is not an error. - -.. ts:def:: DeleteMailboxMessageRequest - - interface DeleteMailboxMessageRequest { - // The message to delete. - message: MailboxMessageRecord; - } - - -.. _wallet-op-sendTalerUriMailboxMessage: - -**sendTalerUriMailboxMessage** - -Send a ``taler://`` URI to the mailbox of a contact. Wallet-core -fetches the public keys of the recipient's mailbox from the contact's -mailbox service, verifies that they match the contact's mailbox -address, encrypts the URI for the recipient and uploads it to the -recipient's mailbox. - -**Request:** - - The request arguments are a `SendTalerUriMailboxMessageRequest` - object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_MAILBOX_UNAVAILABLE``. - -.. ts:def:: SendTalerUriMailboxMessageRequest - - interface SendTalerUriMailboxMessageRequest { - // The recipient contact. - contact: ContactEntry; - - // The taler:// URI to send. - talerUri: string; - } - - -.. _wallet-op-refreshMailbox: - -**refreshMailbox** - -Download new messages from a mailbox service: messages are fetched in -batches, decrypted with the mailbox's private encryption key, stored in -the wallet's local database and deleted on the mailbox service. - -**Request:** - - The request arguments are a `MailboxConfiguration` object for the - mailbox to refresh. - -**Response:** - - On success, the result is a `MailboxMessageRecordsResponse` object - with the newly downloaded messages. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_MAILBOX_UNAVAILABLE``. - -**Details:** - - Each downloaded message is stored like in - :ref:`addMailboxMessage <wallet-op-addMailboxMessage>` and thus - triggers a ``mailbox-message-added`` notification. Messages that - cannot be decrypted are skipped. At most 100 batches are fetched in - one call. - -.. ts:def:: MailboxMessageRecordsResponse - - interface MailboxMessageRecordsResponse { - // Newly downloaded messages. - messages: MailboxMessageRecord[]; - } diff --git a/core/wallet-core/mailbox/add-mailbox-message.rst b/core/wallet-core/mailbox/add-mailbox-message.rst @@ -0,0 +1,25 @@ +.. ts:op:: addMailboxMessage + + Store a mailbox message in the wallet's local database. If a + message with the same ``originMailboxBaseUrl`` and ``talerUri`` + already exists, its ``downloadedAt`` timestamp is updated. + + **Request:** + + The request arguments are an `AddMailboxMessageRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Stores the message in the wallet's local database and emits a + ``mailbox-message-added`` notification. + +.. ts:def:: AddMailboxMessageRequest + + interface AddMailboxMessageRequest { + // The message to store. + message: MailboxMessageRecord; + } diff --git a/core/wallet-core/mailbox/delete-mailbox-message.rst b/core/wallet-core/mailbox/delete-mailbox-message.rst @@ -0,0 +1,30 @@ +.. ts:op:: deleteMailboxMessage + + Delete a mailbox message from the wallet's local database. + + **Request:** + + The request arguments are a `DeleteMailboxMessageRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes the message from the wallet's local database and emits a + ``mailbox-message-deleted`` notification. + + **Details:** + + The message is identified by the ``originMailboxBaseUrl`` and + ``talerUri`` fields of the given message; the ``downloadedAt`` + field is ignored. Deleting a message that does not exist is not + an error. + +.. ts:def:: DeleteMailboxMessageRequest + + interface DeleteMailboxMessageRequest { + // The message to delete. + message: MailboxMessageRecord; + } diff --git a/core/wallet-core/mailbox/get-mailbox-message.rst b/core/wallet-core/mailbox/get-mailbox-message.rst @@ -0,0 +1,43 @@ +.. ts:op:: getMailboxMessage + :read-only: + + Get the mailbox messages stored locally in the wallet. + + Note that the TypeScript client library exposes this operation as + ``GetMailboxMessages`` (with a trailing "s"), while the operation name + sent on the wire is ``getMailboxMessage``. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `MailboxMessagesResponse` object. + + **Details:** + + This operation only reads the wallet's local database; use + :ts:op:`refreshMailbox` to download new messages from the mailbox + service. + +.. ts:def:: MailboxMessagesResponse + + interface MailboxMessagesResponse { + // Locally stored mailbox messages. + messages: MailboxMessageRecord[]; + } + +.. ts:def:: MailboxMessageRecord + + // Record metadata for mailbox messages. + interface MailboxMessageRecord { + // Origin mailbox. + originMailboxBaseUrl: string; + + // Time of download. + downloadedAt: Timestamp; + + // Taler URI in message. + talerUri: string; + } diff --git a/core/wallet-core/mailbox/get-mailbox.rst b/core/wallet-core/mailbox/get-mailbox.rst @@ -0,0 +1,29 @@ +.. ts:op:: getMailbox + :read-only: + + Get the mailbox configuration stored locally for a mailbox service. + This operation only reads the wallet's local database. + + **Request:** + + The request arguments are a `MailboxBaseUrl` object. + + **Response:** + + On success, the result is a `GetMailboxResponse` object. The + ``mailboxConfiguration`` field is absent if the wallet has no + mailbox configuration for the given base URL. + +.. ts:def:: MailboxBaseUrl + + interface MailboxBaseUrl { + // Base URL of the mailbox service. + mailboxBaseUrl: string; + } + +.. ts:def:: GetMailboxResponse + + interface GetMailboxResponse { + // Locally stored configuration for the mailbox service, if any. + mailboxConfiguration?: MailboxConfiguration; + } diff --git a/core/wallet-core/mailbox/initialize-mailbox.rst b/core/wallet-core/mailbox/initialize-mailbox.rst @@ -0,0 +1,58 @@ +.. ts:op:: initializeMailbox + + Create a new mailbox at a mailbox service: wallet-core generates + fresh signing and encryption keys, registers the mailbox with the + service and stores the resulting configuration locally. + + **Request:** + + The request arguments are a `MailboxBaseUrl` object. + + **Response:** + + On success, the result is the newly created `MailboxConfiguration` + object. + + **Side effects:** + + Registers a new mailbox at the mailbox service over the network + and stores the configuration and keys locally. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_MAILBOX_UNAVAILABLE``, ``GENERIC_FORBIDDEN``. + + **Details:** + + A fresh key pair is generated on every call, so each call creates + a new mailbox identity at the service. The registration is made + with an expiration one year in the future. If the mailbox service + requires payment for the registration, the operation still + succeeds and the returned configuration has ``payUri`` set, which + the client can use to pay for the registration. + ``GENERIC_FORBIDDEN`` indicates that the mailbox service refused + the registration. + +.. ts:def:: MailboxConfiguration + + interface MailboxConfiguration { + // Base URL of the mailbox service. + mailboxBaseUrl: string; + + // Private EdDSA signing key of the mailbox. + privateKey: EddsaPrivateKeyString; + + // Private HPKE encryption key of the mailbox. + privateEncryptionKey: string; + + // Expiration of the current mailbox registration. + expiration: Timestamp; + + // Mailbox address (hash of the public signing key). + hAddress: string; + + // Set when the mailbox service requires payment to complete + // the registration. + payUri?: TalerUri; + } diff --git a/core/wallet-core/mailbox/refresh-mailbox.rst b/core/wallet-core/mailbox/refresh-mailbox.rst @@ -0,0 +1,41 @@ +.. ts:op:: refreshMailbox + + Download new messages from a mailbox service: messages are fetched + in batches, decrypted with the mailbox's private encryption key, + stored in the wallet's local database and deleted on the mailbox + service. + + **Request:** + + The request arguments are a `MailboxConfiguration` object for the + mailbox to refresh. + + **Response:** + + On success, the result is a `MailboxMessageRecordsResponse` object + with the newly downloaded messages. + + **Side effects:** + + Downloads new messages from the mailbox service over the network, + stores them in the wallet's local database and deletes them on the + service. A ``mailbox-message-added`` notification is emitted for + each stored message. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_MAILBOX_UNAVAILABLE``. + + **Details:** + + Each downloaded message is stored like in + :ts:op:`addMailboxMessage`. Messages that cannot be decrypted are + skipped. At most 100 batches are fetched in one call. + +.. ts:def:: MailboxMessageRecordsResponse + + interface MailboxMessageRecordsResponse { + // Newly downloaded messages. + messages: MailboxMessageRecord[]; + } diff --git a/core/wallet-core/mailbox/send-taler-uri-mailbox-message.rst b/core/wallet-core/mailbox/send-taler-uri-mailbox-message.rst @@ -0,0 +1,36 @@ +.. ts:op:: sendTalerUriMailboxMessage + + Send a ``taler://`` URI to the mailbox of a contact. Wallet-core + fetches the public keys of the recipient's mailbox from the + contact's mailbox service, verifies that they match the contact's + mailbox address, encrypts the URI for the recipient and uploads it + to the recipient's mailbox. + + **Request:** + + The request arguments are a `SendTalerUriMailboxMessageRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Sends the ``taler://`` URI as a message via the contact's mailbox + service over the network. Does not change local wallet state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_MAILBOX_UNAVAILABLE``. + +.. ts:def:: SendTalerUriMailboxMessageRequest + + interface SendTalerUriMailboxMessageRequest { + // The recipient contact. + contact: ContactEntry; + + // The taler:// URI to send. + talerUri: string; + } diff --git a/core/wallet-core/notifications.rst b/core/wallet-core/notifications.rst @@ -2,7 +2,7 @@ **coin-recovery-progress** -Emitted while the :ref:`testingRecoverCoins <wallet-op-testingRecoverCoins>` +Emitted while the :ts:op:`testingRecoverCoins` operation scans an exchange for coins that belong to the wallet and recovers them. The ``progressToken`` matches the progress token of the initiating request. @@ -46,7 +46,7 @@ The payload is a `CoinRecoveryProgressNotification` object. Invalidates balance data, including flags and refresh information. The monetary amounts need not have changed, for example when a refresh cost becomes ready. Clients should re-query -:ref:`getBalances <wallet-op-getBalances>` in response. +:ts:op:`getBalances` in response. The payload is a `BalanceChangeNotification` object. @@ -74,7 +74,7 @@ The payload is a `BalanceChangeNotification` object. Emitted when a bank account known to the wallet was added, changed or deleted. Clients should re-query -:ref:`listBankAccounts <wallet-op-listBankAccounts>`. +:ts:op:`listBankAccounts`. The payload is a `BankAccountChangeNotification` object. @@ -110,7 +110,7 @@ The payload is a `BackupOperationErrorNotification` object. **contact-added** Emitted when a contact was added to the wallet's address book. Clients -should re-query :ref:`getContacts <wallet-op-getContacts>`. +should re-query :ts:op:`getContacts`. The payload is a `ContactAddedNotification` object. @@ -129,7 +129,7 @@ The payload is a `ContactAddedNotification` object. **contact-deleted** Emitted when a contact was deleted from the wallet's address book. Clients -should re-query :ref:`getContacts <wallet-op-getContacts>`. +should re-query :ts:op:`getContacts`. The payload is a `ContactDeletedNotification` object. @@ -475,9 +475,9 @@ The payload is a `RequestObservabilityEventNotification` object. Emitted when an attempt of a long-running request with a ``progressToken`` failed and will be retried automatically after ``nextRetryDelay``. The client can cancel the request with -:ref:`cancelProgressToken <wallet-op-cancelProgressToken>` or trigger an +:ts:op:`cancelProgressToken` or trigger an immediate retry with -:ref:`retryProgressTokenNow <wallet-op-retryProgressTokenNow>`. +:ts:op:`retryProgressTokenNow`. The payload is a `RequestProgressNotification` object. diff --git a/core/wallet-core/p2p.rst b/core/wallet-core/p2p.rst @@ -1,548 +0,0 @@ -.. _wallet-op-preparePeerPushCredit: - -**preparePeerPushCredit** - -Check an incoming peer push payment. The payment is identified either -by a ``taler://pay-push/`` URI received from the sender or by the -``transactionId`` of an existing peer-push-credit transaction. - -**Request:** - - The request body must be a `PreparePeerPushCreditRequest` object. - Either ``talerUri`` or ``transactionId`` must be specified. - -**Response:** - - On success, the result is a `PreparePeerPushCreditResponse` object, - carrying the contract terms and the amounts the wallet would receive, - so that the user can review the payment before accepting it with - :ref:`confirmPeerPushCredit <wallet-op-confirmPeerPushCredit>`. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PEER_CONTRACT_NOT_FOUND``, - ``WALLET_PEER_PUSH_CREDIT_PURSE_GONE``, ``WALLET_TALER_URI_MALFORMED``, - ``WALLET_TRANSACTION_NOT_FOUND``. - -**Details:** - - When called with a ``talerUri`` for a payment that is not yet known - to the wallet, wallet-core downloads the contract from the exchange, - checks the status of the purse and creates the peer-push-credit - transaction. Calling the operation again with the same URI or with - the resulting ``transactionId`` returns the existing state. - -.. ts:def:: PreparePeerPushCreditRequest - - // Either talerUri or transactionId must be specified. - interface PreparePeerPushCreditRequest { - talerUri?: string; - transactionId?: string; - - progressToken?: string; - } - -.. ts:def:: PreparePeerPushCreditResponse - - interface PreparePeerPushCreditResponse { - contractTerms: PeerContractTerms; - amountRaw: AmountString; - amountEffective: AmountString; - - transactionId: TransactionIdStr; - - // State of the existing or newly created transaction. - txState: TransactionState; - - exchangeBaseUrl: string; - - scopeInfo: ScopeInfo; - - // Deprecated. - amount: AmountString; - } - -.. ts:def:: PeerContractTerms - - // Contract terms between two wallets (as opposed to a merchant and - // wallet). - interface PeerContractTerms { - amount: AmountString; - summary: string; - icon_id?: string; - purse_expiration: TalerProtocolTimestamp; - } - - -.. _wallet-op-checkPeerPushDebit: - -**checkPeerPushDebit** - -This operation is **deprecated**. Use :ref:`checkPeerPushDebitV2 -<wallet-op-checkPeerPushDebitV2>` instead. - -Check if initiating a peer push payment is possible based on the funds -in the wallet. Unlike the V2 operation, an insufficient balance is -reported via the ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE`` -error instead of a typed result. - -**Request:** - - The request body must be a `CheckPeerPushDebitRequest` object. - -**Response:** - - On success, the result is a `CheckPeerPushDebitOkResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE``, - ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. - -.. ts:def:: CheckPeerPushDebitRequest - - interface CheckPeerPushDebitRequest { - // Preferred exchange to use for the p2p payment. - exchangeBaseUrl?: string; - - // Instructed amount. - amount: AmountString; - - // Restrict the scope of funds that can be spent via the given - // scope info. - restrictScope?: ScopeInfo; - - progressToken?: string; - } - -.. ts:def:: CheckPeerPushDebitOkResponse - - interface CheckPeerPushDebitOkResponse { - type: "ok"; - - amountRaw: AmountString; - - amountEffective: AmountString; - - // Exchange base URL. - exchangeBaseUrl: string; - - // Maximum expiration date, based on how close the coins used for - // the payment are to expiry. The value is based on when the - // wallet would typically refresh the coins on its own, leaving - // enough time to get a refund for the push payment and refresh - // the coin. - maxExpirationDate: TalerProtocolTimestamp; - - // Default expiration, as given by the exchange - // (or 1 week if the exchange does not specify it). - defaultExpiration: TalerProtocolDuration; - - // Opaque description of the values reviewed by the caller. - // Passing this back to initiatePeerPushDebit makes wallet-core - // reject the operation when coin selection, fees or the selected - // exchange changed in the meantime. Optional for compatibility - // with older wallet-core implementations. - peerPushDebitQuote?: string; - } - - -.. _wallet-op-checkPeerPushDebitV2: - -**checkPeerPushDebitV2** - -Check if initiating a peer push payment is possible based on the funds -in the wallet. - -**Request:** - - The request body must be a `CheckPeerPushDebitRequest` object. - -**Response:** - - On success, the result is a `CheckPeerPushDebitResponse` object, a - discriminated union on the ``type`` field: ``"ok"`` - (`CheckPeerPushDebitOkResponse`) carries the reviewed amounts and a - quote for :ref:`initiatePeerPushDebit <wallet-op-initiatePeerPushDebit>`; - ``"insufficient-balance"`` - (`CheckPeerPushDebitInsufficientBalanceResponse`) explains why the - wallet cannot cover the payment. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE``, - ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. - -.. ts:def:: CheckPeerPushDebitResponse - - type CheckPeerPushDebitResponse = - | CheckPeerPushDebitOkResponse - | CheckPeerPushDebitInsufficientBalanceResponse; - -.. ts:def:: CheckPeerPushDebitInsufficientBalanceResponse - - interface CheckPeerPushDebitInsufficientBalanceResponse { - type: "insufficient-balance"; - - insufficientBalanceDetails: PaymentInsufficientBalanceDetails; - } - - -.. _wallet-op-initiatePeerPushDebit: - -**initiatePeerPushDebit** - -Initiate an outgoing peer push payment. - -**Request:** - - The request body must be an `InitiatePeerPushDebitRequest` object. - -**Response:** - - On success, the result is an `InitiatePeerPushDebitResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE``, - ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - ``exchangeBaseUrl`` and ``restrictScope`` are mutually exclusive. - The ``purse_expiration`` in ``partialContractTerms`` must be finite - and in the future. When a ``peerPushDebitQuote`` obtained from - :ref:`checkPeerPushDebitV2 <wallet-op-checkPeerPushDebitV2>` is - passed, an explicit expiration is required, and wallet-core rejects - the request when it does not match the quote or when the coin - selection, fees or the selected exchange changed since the quote was - made. - - The ``taler://pay-push/`` URI to hand to the receiver is not part of - the response; it is available from the transaction (see - :ref:`getTransactionById <wallet-op-getTransactionById>`) once the - transaction is pending. - -.. ts:def:: InitiatePeerPushDebitRequest - - interface InitiatePeerPushDebitRequest { - exchangeBaseUrl?: string; - - // Restrict the scope of funds that can be spent via the given - // scope info. - restrictScope?: ScopeInfo; - - // Quote returned by checkPeerPushDebitV2. - peerPushDebitQuote?: string; - - partialContractTerms: PartialPeerContractTerms; - } - -.. ts:def:: PartialPeerContractTerms - - interface PartialPeerContractTerms { - amount: AmountString; - summary: string; - icon_id?: string; - purse_expiration?: TalerProtocolTimestamp; - } - -.. ts:def:: InitiatePeerPushDebitResponse - - interface InitiatePeerPushDebitResponse { - exchangeBaseUrl: string; - pursePub: string; - mergePriv: string; - contractPriv: string; - transactionId: TransactionIdStr; - } - - -.. _wallet-op-confirmPeerPushCredit: - -**confirmPeerPushCredit** - -Accept an incoming peer push payment, after reviewing it with -:ref:`preparePeerPushCredit <wallet-op-preparePeerPushCredit>`. - -**Request:** - - The request body must be a `ConfirmPeerPushCreditRequest` object. - -**Response:** - - On success, the result is an `AcceptPeerPushPaymentResponse` object. - Confirmation is idempotent: confirming a transaction that already - left the review state returns the same ``transactionId``. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_KYC_LIMIT_EXCEEDED``, ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. - -.. ts:def:: ConfirmPeerPushCreditRequest - - interface ConfirmPeerPushCreditRequest { - transactionId: string; - - progressToken?: string; - } - -.. ts:def:: AcceptPeerPushPaymentResponse - - interface AcceptPeerPushPaymentResponse { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-checkPeerPullCredit: - -**checkPeerPullCredit** - -Check fees for an outgoing peer pull payment, i.e. a payment request -that this wallet creates for another wallet to pay. - -**Request:** - - The request body must be a `CheckPeerPullCreditRequest` object. - Either ``exchangeBaseUrl`` or ``restrictScope`` must be specified. - -**Response:** - - On success, the result is a `CheckPeerPullCreditResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. - -.. ts:def:: CheckPeerPullCreditRequest - - interface CheckPeerPullCreditRequest { - // Require using this particular exchange for this operation. - exchangeBaseUrl?: string; - - restrictScope?: ScopeInfo; - - amount: AmountString; - - progressToken?: string; - } - -.. ts:def:: CheckPeerPullCreditResponse - - interface CheckPeerPullCreditResponse { - exchangeBaseUrl: string; - amountRaw: AmountString; - amountEffective: AmountString; - - // Wallet-selected default expiration for the request. - defaultExpiration: TalerProtocolDuration; - - // Number of coins that will be used; can be used by the UI to - // warn if excessively large. - numCoins: number; - } - - -.. _wallet-op-initiatePeerPullCredit: - -**initiatePeerPullCredit** - -Initiate an outgoing peer pull payment: create a payment request -(invoice) that another wallet can pay. - -**Request:** - - The request body must be an `InitiatePeerPullCreditRequest` object. - -**Response:** - - On success, the result is an `InitiatePeerPullCreditResponse` - object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_KYC_LIMIT_EXCEEDED``, - ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, ``WALLET_CORE_API_BAD_REQUEST``, - ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. - -**Details:** - - The ``taler://pay-pull/`` URI to hand to the payer is available from - the transaction (see :ref:`getTransactionById - <wallet-op-getTransactionById>`) once the transaction is in the - appropriate state; the ``talerUri`` field of the response is - deprecated because it is not necessarily valid yet. - -.. ts:def:: InitiatePeerPullCreditRequest - - interface InitiatePeerPullCreditRequest { - exchangeBaseUrl?: string; - partialContractTerms: PeerContractTerms; - - progressToken?: string; - } - -.. ts:def:: InitiatePeerPullCreditResponse - - interface InitiatePeerPullCreditResponse { - // Taler URI for the other party to make the requested payment. - // Deprecated: not necessarily valid yet until the transaction is - // in the right state. - talerUri?: string; - - transactionId: TransactionIdStr; - } - - -.. _wallet-op-preparePeerPullDebit: - -**preparePeerPullDebit** - -Prepare for an incoming peer pull payment: another wallet requests to -be paid by this wallet. The request is identified either by a -``taler://pay-pull/`` URI received from the other party or by the -``transactionId`` of an existing peer-pull-debit transaction. - -**Request:** - - The request body must be a `PreparePeerPullDebitRequest` object. - Either ``talerUri`` or ``transactionId`` must be specified. - -**Response:** - - On success, the result is a `PreparePeerPullDebitResponse` object, - carrying the contract terms and the amounts, so that the user can - review the request before paying with :ref:`confirmPeerPullDebit - <wallet-op-confirmPeerPullDebit>`. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PEER_CONTRACT_NOT_FOUND``, - ``WALLET_PEER_PULL_DEBIT_PURSE_GONE``, - ``WALLET_PEER_PULL_DEBIT_ALREADY_PAID``, - ``WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE``, - ``WALLET_TALER_URI_MALFORMED``, ``WALLET_TRANSACTION_NOT_FOUND``. - -.. ts:def:: PreparePeerPullDebitRequest - - // Either talerUri or transactionId must be specified. - interface PreparePeerPullDebitRequest { - talerUri?: string; - transactionId?: string; - - progressToken?: string; - } - -.. ts:def:: PreparePeerPullDebitResponse - - interface PreparePeerPullDebitResponse { - contractTerms: PeerContractTerms; - - amountRaw: AmountString; - amountEffective: AmountString; - - transactionId: TransactionIdStr; - - // State of the existing or newly created transaction. - txState: TransactionState; - - exchangeBaseUrl: string; - - scopeInfo: ScopeInfo; - - // Deprecated: redundant field with bad name, will be removed soon. - amount: AmountString; - } - - -.. _wallet-op-confirmPeerPullDebit: - -**confirmPeerPullDebit** - -Accept an incoming peer pull payment (i.e. pay the other party), after -reviewing the request with :ref:`preparePeerPullDebit -<wallet-op-preparePeerPullDebit>`. - -**Request:** - - The request body must be a `ConfirmPeerPullDebitRequest` object. - -**Response:** - - On success, the result is an `AcceptPeerPullPaymentResponse` object. - Confirmation is idempotent: confirming a transaction that already - left the review state returns the same ``transactionId``. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE``, - ``WALLET_TRANSACTION_NOT_FOUND``. - -.. ts:def:: ConfirmPeerPullDebitRequest - - interface ConfirmPeerPullDebitRequest { - transactionId: TransactionIdStr; - } - -.. ts:def:: AcceptPeerPullPaymentResponse - - interface AcceptPeerPullPaymentResponse { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-getMaxPeerPushDebitAmount: - -**getMaxPeerPushDebitAmount** - -Query the maximum amount that can currently be sent via a peer push -payment in the given currency. - -**Request:** - - The request body must be a `GetMaxPeerPushDebitAmountRequest` object. - -**Response:** - - On success, the result is a `GetMaxPeerPushDebitAmountResponse` - object. - -**Details:** - - The result is the best per-exchange maximum across the exchanges - known to the wallet for the currency (restricted to ``restrictScope`` - when given, and including the expected outputs of pending refresh - operations). ``effectiveAmount`` is the total balance effect on the - wallet and ``rawAmount`` the amount the receiver would get after - fees. When no exchange can serve the payment, zero amounts are - returned and ``exchangeBaseUrl`` is omitted. - -.. ts:def:: GetMaxPeerPushDebitAmountRequest - - interface GetMaxPeerPushDebitAmountRequest { - currency: string; - - // Preferred exchange to use for the p2p payment. - exchangeBaseUrl?: string; - - restrictScope?: ScopeInfo; - } - -.. ts:def:: GetMaxPeerPushDebitAmountResponse - - interface GetMaxPeerPushDebitAmountResponse { - effectiveAmount: AmountString; - rawAmount: AmountString; - exchangeBaseUrl?: string; - } diff --git a/core/wallet-core/p2p/check-peer-pull-credit.rst b/core/wallet-core/p2p/check-peer-pull-credit.rst @@ -0,0 +1,51 @@ +.. ts:op:: checkPeerPullCredit + + Check fees for an outgoing peer pull payment, i.e. a payment request + that this wallet creates for another wallet to pay. + + **Request:** + + The request body must be a `CheckPeerPullCreditRequest` object. + Either ``exchangeBaseUrl`` or ``restrictScope`` must be specified. + + **Response:** + + On success, the result is a `CheckPeerPullCreditResponse` object. + + **Side effects:** + + May refresh the exchange's key material over the network and writes + denomination verification results. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. + +.. ts:def:: CheckPeerPullCreditRequest + + interface CheckPeerPullCreditRequest { + // Require using this particular exchange for this operation. + exchangeBaseUrl?: string; + + restrictScope?: ScopeInfo; + + amount: AmountString; + + progressToken?: string; + } + +.. ts:def:: CheckPeerPullCreditResponse + + interface CheckPeerPullCreditResponse { + exchangeBaseUrl: string; + amountRaw: AmountString; + amountEffective: AmountString; + + // Wallet-selected default expiration for the request. + defaultExpiration: TalerProtocolDuration; + + // Number of coins that will be used; can be used by the UI to + // warn if excessively large. + numCoins: number; + } diff --git a/core/wallet-core/p2p/check-peer-push-debit-v2.rst b/core/wallet-core/p2p/check-peer-push-debit-v2.rst @@ -0,0 +1,42 @@ +.. ts:op:: checkPeerPushDebitV2 + + Check if initiating a peer push payment is possible based on the + funds in the wallet. + + **Request:** + + The request body must be a `CheckPeerPushDebitRequest` object. + + **Response:** + + On success, the result is a `CheckPeerPushDebitResponse` object, a + discriminated union on the ``type`` field: ``"ok"`` + (`CheckPeerPushDebitOkResponse`) carries the reviewed amounts and a + quote for :ts:op:`initiatePeerPushDebit`; + ``"insufficient-balance"`` + (`CheckPeerPushDebitInsufficientBalanceResponse`) explains why the + wallet cannot cover the payment. + + **Side effects:** + + May refresh the selected exchange's key material over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE``, + ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. + +.. ts:def:: CheckPeerPushDebitResponse + + type CheckPeerPushDebitResponse = + | CheckPeerPushDebitOkResponse + | CheckPeerPushDebitInsufficientBalanceResponse; + +.. ts:def:: CheckPeerPushDebitInsufficientBalanceResponse + + interface CheckPeerPushDebitInsufficientBalanceResponse { + type: "insufficient-balance"; + + insufficientBalanceDetails: PaymentInsufficientBalanceDetails; + } diff --git a/core/wallet-core/p2p/check-peer-push-debit.rst b/core/wallet-core/p2p/check-peer-push-debit.rst @@ -0,0 +1,76 @@ +.. ts:op:: checkPeerPushDebit + :deprecated: + + This operation is **deprecated**. Use + :ts:op:`checkPeerPushDebitV2` instead. + + Check if initiating a peer push payment is possible based on the + funds in the wallet. Unlike the V2 operation, an insufficient + balance is reported via the + ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE`` error instead of a + typed result. + + **Request:** + + The request body must be a `CheckPeerPushDebitRequest` object. + + **Response:** + + On success, the result is a `CheckPeerPushDebitOkResponse` object. + + **Side effects:** + + May refresh the selected exchange's key material over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE``, + ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. + +.. ts:def:: CheckPeerPushDebitRequest + + interface CheckPeerPushDebitRequest { + // Preferred exchange to use for the p2p payment. + exchangeBaseUrl?: string; + + // Instructed amount. + amount: AmountString; + + // Restrict the scope of funds that can be spent via the given + // scope info. + restrictScope?: ScopeInfo; + + progressToken?: string; + } + +.. ts:def:: CheckPeerPushDebitOkResponse + + interface CheckPeerPushDebitOkResponse { + type: "ok"; + + amountRaw: AmountString; + + amountEffective: AmountString; + + // Exchange base URL. + exchangeBaseUrl: string; + + // Maximum expiration date, based on how close the coins used for + // the payment are to expiry. The value is based on when the + // wallet would typically refresh the coins on its own, leaving + // enough time to get a refund for the push payment and refresh + // the coin. + maxExpirationDate: TalerProtocolTimestamp; + + // Default expiration, as given by the exchange + // (or 1 week if the exchange does not specify it). + defaultExpiration: TalerProtocolDuration; + + // Opaque description of the values reviewed by the caller. + // Passing this back to initiatePeerPushDebit makes wallet-core + // reject the operation when coin selection, fees or the selected + // exchange changed in the meantime. Optional for compatibility + // with older wallet-core implementations. + peerPushDebitQuote?: string; + } diff --git a/core/wallet-core/p2p/confirm-peer-pull-debit.rst b/core/wallet-core/p2p/confirm-peer-pull-debit.rst @@ -0,0 +1,37 @@ +.. ts:op:: confirmPeerPullDebit + + Accept an incoming peer pull payment (i.e. pay the other party), + after reviewing the request with :ts:op:`preparePeerPullDebit`. + + **Request:** + + The request body must be a `ConfirmPeerPullDebitRequest` object. + + **Response:** + + On success, the result is an `AcceptPeerPullPaymentResponse` + object. Confirmation is idempotent: confirming a transaction that + already left the review state returns the same ``transactionId``. + + **Side effects:** + + Pays the requested peer pull payment to the other wallet over the + network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE``, + ``WALLET_TRANSACTION_NOT_FOUND``. + +.. ts:def:: ConfirmPeerPullDebitRequest + + interface ConfirmPeerPullDebitRequest { + transactionId: TransactionIdStr; + } + +.. ts:def:: AcceptPeerPullPaymentResponse + + interface AcceptPeerPullPaymentResponse { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/p2p/confirm-peer-push-credit.rst b/core/wallet-core/p2p/confirm-peer-push-credit.rst @@ -0,0 +1,40 @@ +.. ts:op:: confirmPeerPushCredit + + Accept an incoming peer push payment, after reviewing it with + :ts:op:`preparePeerPushCredit`. + + **Request:** + + The request body must be a `ConfirmPeerPushCreditRequest` object. + + **Response:** + + On success, the result is an `AcceptPeerPushPaymentResponse` + object. Confirmation is idempotent: confirming a transaction that + already left the review state returns the same ``transactionId``. + + **Side effects:** + + Confirms an incoming peer push payment; the wallet withdraws the + funds from the purse over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_KYC_LIMIT_EXCEEDED``, ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. + +.. ts:def:: ConfirmPeerPushCreditRequest + + interface ConfirmPeerPushCreditRequest { + transactionId: string; + + progressToken?: string; + } + +.. ts:def:: AcceptPeerPushPaymentResponse + + interface AcceptPeerPushPaymentResponse { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/p2p/get-max-peer-push-debit-amount.rst b/core/wallet-core/p2p/get-max-peer-push-debit-amount.rst @@ -0,0 +1,44 @@ +.. ts:op:: getMaxPeerPushDebitAmount + :read-only: + + Query the maximum amount that can currently be sent via a peer push + payment in the given currency. + + **Request:** + + The request body must be a `GetMaxPeerPushDebitAmountRequest` + object. + + **Response:** + + On success, the result is a `GetMaxPeerPushDebitAmountResponse` + object. + + **Details:** + + The result is the best per-exchange maximum across the exchanges + known to the wallet for the currency (restricted to ``restrictScope`` + when given, and including the expected outputs of pending refresh + operations). ``effectiveAmount`` is the total balance effect on the + wallet and ``rawAmount`` the amount the receiver would get after + fees. When no exchange can serve the payment, zero amounts are + returned and ``exchangeBaseUrl`` is omitted. + +.. ts:def:: GetMaxPeerPushDebitAmountRequest + + interface GetMaxPeerPushDebitAmountRequest { + currency: string; + + // Preferred exchange to use for the p2p payment. + exchangeBaseUrl?: string; + + restrictScope?: ScopeInfo; + } + +.. ts:def:: GetMaxPeerPushDebitAmountResponse + + interface GetMaxPeerPushDebitAmountResponse { + effectiveAmount: AmountString; + rawAmount: AmountString; + exchangeBaseUrl?: string; + } diff --git a/core/wallet-core/p2p/initiate-peer-pull-credit.rst b/core/wallet-core/p2p/initiate-peer-pull-credit.rst @@ -0,0 +1,51 @@ +.. ts:op:: initiatePeerPullCredit + + Initiate an outgoing peer pull payment: create a payment request + (invoice) that another wallet can pay. + + **Request:** + + The request body must be an `InitiatePeerPullCreditRequest` object. + + **Response:** + + On success, the result is an `InitiatePeerPullCreditResponse` + object. + + **Side effects:** + + Creates an outgoing peer pull payment request transaction. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_KYC_LIMIT_EXCEEDED``, + ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, ``WALLET_CORE_API_BAD_REQUEST``, + ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. + + **Details:** + + The ``taler://pay-pull/`` URI to hand to the payer is available from + the transaction (see :ts:op:`getTransactionById`) once the + transaction is in the appropriate state; the ``talerUri`` field of + the response is deprecated because it is not necessarily valid yet. + +.. ts:def:: InitiatePeerPullCreditRequest + + interface InitiatePeerPullCreditRequest { + exchangeBaseUrl?: string; + partialContractTerms: PeerContractTerms; + + progressToken?: string; + } + +.. ts:def:: InitiatePeerPullCreditResponse + + interface InitiatePeerPullCreditResponse { + // Taler URI for the other party to make the requested payment. + // Deprecated: not necessarily valid yet until the transaction is + // in the right state. + talerUri?: string; + + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/p2p/initiate-peer-push-debit.rst b/core/wallet-core/p2p/initiate-peer-push-debit.rst @@ -0,0 +1,71 @@ +.. ts:op:: initiatePeerPushDebit + + Initiate an outgoing peer push payment. + + **Request:** + + The request body must be an `InitiatePeerPushDebitRequest` object. + + **Response:** + + On success, the result is an `InitiatePeerPushDebitResponse` + object. + + **Side effects:** + + Creates an outgoing peer push payment transaction, locking the + coins. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE``, + ``WALLET_NO_SUITABLE_EXCHANGE``, ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + ``exchangeBaseUrl`` and ``restrictScope`` are mutually exclusive. + The ``purse_expiration`` in ``partialContractTerms`` must be finite + and in the future. When a ``peerPushDebitQuote`` obtained from + :ts:op:`checkPeerPushDebitV2` is passed, an explicit expiration is + required, and wallet-core rejects the request when it does not match + the quote or when the coin selection, fees or the selected exchange + changed since the quote was made. + + The ``taler://pay-push/`` URI to hand to the receiver is not part of + the response; it is available from the transaction (see + :ts:op:`getTransactionById`) once the transaction is pending. + +.. ts:def:: InitiatePeerPushDebitRequest + + interface InitiatePeerPushDebitRequest { + exchangeBaseUrl?: string; + + // Restrict the scope of funds that can be spent via the given + // scope info. + restrictScope?: ScopeInfo; + + // Quote returned by checkPeerPushDebitV2. + peerPushDebitQuote?: string; + + partialContractTerms: PartialPeerContractTerms; + } + +.. ts:def:: PartialPeerContractTerms + + interface PartialPeerContractTerms { + amount: AmountString; + summary: string; + icon_id?: string; + purse_expiration?: TalerProtocolTimestamp; + } + +.. ts:def:: InitiatePeerPushDebitResponse + + interface InitiatePeerPushDebitResponse { + exchangeBaseUrl: string; + pursePub: string; + mergePriv: string; + contractPriv: string; + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/p2p/prepare-peer-pull-debit.rst b/core/wallet-core/p2p/prepare-peer-pull-debit.rst @@ -0,0 +1,62 @@ +.. ts:op:: preparePeerPullDebit + + Prepare for an incoming peer pull payment: another wallet requests + to be paid by this wallet. The request is identified either by a + ``taler://pay-pull/`` URI received from the other party or by the + ``transactionId`` of an existing peer-pull-debit transaction. + + **Request:** + + The request body must be a `PreparePeerPullDebitRequest` object. + Either ``talerUri`` or ``transactionId`` must be specified. + + **Response:** + + On success, the result is a `PreparePeerPullDebitResponse` object, + carrying the contract terms and the amounts, so that the user can + review the request before paying with :ts:op:`confirmPeerPullDebit`. + + **Side effects:** + + Fetches the contract and purse status from the exchange over the + network and creates a peer pull debit transaction in dialog state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PEER_CONTRACT_NOT_FOUND``, + ``WALLET_PEER_PULL_DEBIT_PURSE_GONE``, + ``WALLET_PEER_PULL_DEBIT_ALREADY_PAID``, + ``WALLET_PEER_PULL_PAYMENT_INSUFFICIENT_BALANCE``, + ``WALLET_TALER_URI_MALFORMED``, ``WALLET_TRANSACTION_NOT_FOUND``. + +.. ts:def:: PreparePeerPullDebitRequest + + // Either talerUri or transactionId must be specified. + interface PreparePeerPullDebitRequest { + talerUri?: string; + transactionId?: string; + + progressToken?: string; + } + +.. ts:def:: PreparePeerPullDebitResponse + + interface PreparePeerPullDebitResponse { + contractTerms: PeerContractTerms; + + amountRaw: AmountString; + amountEffective: AmountString; + + transactionId: TransactionIdStr; + + // State of the existing or newly created transaction. + txState: TransactionState; + + exchangeBaseUrl: string; + + scopeInfo: ScopeInfo; + + // Deprecated: redundant field with bad name, will be removed soon. + amount: AmountString; + } diff --git a/core/wallet-core/p2p/prepare-peer-push-credit.rst b/core/wallet-core/p2p/prepare-peer-push-credit.rst @@ -0,0 +1,77 @@ +.. ts:op:: preparePeerPushCredit + + Check an incoming peer push payment. The payment is identified + either by a ``taler://pay-push/`` URI received from the sender or by + the ``transactionId`` of an existing peer-push-credit transaction. + + **Request:** + + The request body must be a `PreparePeerPushCreditRequest` object. + Either ``talerUri`` or ``transactionId`` must be specified. + + **Response:** + + On success, the result is a `PreparePeerPushCreditResponse` object, + carrying the contract terms and the amounts the wallet would + receive, so that the user can review the payment before accepting it + with :ts:op:`confirmPeerPushCredit`. + + **Side effects:** + + Fetches the contract and purse status from the exchange over the + network and creates an incoming peer push payment transaction in + dialog state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PEER_CONTRACT_NOT_FOUND``, + ``WALLET_PEER_PUSH_CREDIT_PURSE_GONE``, ``WALLET_TALER_URI_MALFORMED``, + ``WALLET_TRANSACTION_NOT_FOUND``. + + **Details:** + + Calling the operation again with the same ``talerUri`` or with the + resulting ``transactionId`` returns the state of the existing + transaction instead of fetching the contract again. + +.. ts:def:: PreparePeerPushCreditRequest + + // Either talerUri or transactionId must be specified. + interface PreparePeerPushCreditRequest { + talerUri?: string; + transactionId?: string; + + progressToken?: string; + } + +.. ts:def:: PreparePeerPushCreditResponse + + interface PreparePeerPushCreditResponse { + contractTerms: PeerContractTerms; + amountRaw: AmountString; + amountEffective: AmountString; + + transactionId: TransactionIdStr; + + // State of the existing or newly created transaction. + txState: TransactionState; + + exchangeBaseUrl: string; + + scopeInfo: ScopeInfo; + + // Deprecated. + amount: AmountString; + } + +.. ts:def:: PeerContractTerms + + // Contract terms between two wallets (as opposed to a merchant and + // wallet). + interface PeerContractTerms { + amount: AmountString; + summary: string; + icon_id?: string; + purse_expiration: TalerProtocolTimestamp; + } diff --git a/core/wallet-core/payments.rst b/core/wallet-core/payments.rst @@ -1,985 +0,0 @@ -.. _wallet-op-getChoicesForPayment: - -**getChoicesForPayment** - -Get the list of contract choices for a payment transaction in the dialog -(confirmation) state, together with information on whether each choice -can be paid with the funds available in the wallet, and whether a -specific choice should be paid automatically without user confirmation, -based on the user's configuration or the type of payment requested. - -For a contract v1 order, the ``choices`` array of the result mirrors the -choices of the contract. For a contract v0 order, which has no choices, -it contains a single choice with no inputs/outputs. - -**Request:** - - The ``args`` must be a `GetChoicesForPaymentRequest` object. - -**Response:** - - On success, the result is a `GetChoicesForPaymentResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, - ``WALLET_TRANSACTION_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. - - The ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED`` error is returned - while the contract terms of the payment have not been downloaded yet; - the caller should wait for the corresponding transaction state - transition and try again. - -.. ts:def:: GetChoicesForPaymentRequest - - interface GetChoicesForPaymentRequest { - // Transaction identifier of the payment. - transactionId: string; - - // Force a particular coin selection when evaluating - // whether the choices are payable. - forcedCoinSel?: ForcedCoinSel; - } - -.. ts:def:: ForcedCoinSel - - interface ForcedCoinSel { - coins: { - value: AmountString; - contribution: AmountString; - }[]; - } - -.. ts:def:: GetChoicesForPaymentResult - - type GetChoicesForPaymentResult = { - // Details for all choices in the contract. - // - // The index in this array corresponds to the choice - // index in the original contract v1. For contract v0 - // orders, it will only contain a single choice with no - // inputs/outputs. - choices: ChoiceSelectionDetail[]; - - // Index of the choice in the `choices` array to present - // to the user as default. - // - // Won't be set if no default selection is configured - // or no choice is payable; otherwise it will always - // be 0 for v0 orders. - defaultChoiceIndex?: number; - - // Whether the choice referenced by `automaticExecutableIndex` - // should be confirmed automatically without user interaction. - // - // If true, the wallet should call `confirmPay` immediately - // afterwards; if false, the user should first be prompted - // to select and confirm a choice. Undefined when no - // choices are payable. - automaticExecution?: boolean; - - // Index of the choice that would be set to automatically - // execute if the choice was payable. When `automaticExecution` - // is set to true, the payment should be confirmed with this - // choice index without user interaction. - automaticExecutableIndex?: number; - - // Data extracted from the contract terms that - // is relevant for payment processing in the wallet. - contractTerms: MerchantContractTerms; - }; - -.. ts:def:: ChoiceSelectionDetail - - type ChoiceSelectionDetail = - | ChoiceSelectionDetailPaymentPossible - | ChoiceSelectionDetailInsufficientBalance; - -.. ts:def:: ChoiceSelectionDetailPaymentPossible - - interface ChoiceSelectionDetailPaymentPossible { - status: ChoiceSelectionDetailType.PaymentPossible; - - // Amount requested by the contract for this choice. - amountRaw: AmountString; - - // Total cost of this choice for the wallet, including fees. - amountEffective: AmountString; - - // Scope of the coins that would be spent, if known. - scopeInfo: ScopeInfo | undefined; - - tokenDetails?: PaymentTokenAvailabilityDetails; - } - -.. ts:def:: ChoiceSelectionDetailInsufficientBalance - - interface ChoiceSelectionDetailInsufficientBalance { - status: ChoiceSelectionDetailType.InsufficientBalance; - - // Amount requested by the contract for this choice. - amountRaw: AmountString; - - balanceDetails?: PaymentInsufficientBalanceDetails; - tokenDetails?: PaymentTokenAvailabilityDetails; - } - -.. ts:def:: ChoiceSelectionDetailType - - type ChoiceSelectionDetailType = - | "payment-possible" - | "insufficient-balance"; - -.. ts:def:: PaymentTokenAvailabilityDetails - - interface PaymentTokenAvailabilityDetails { - // Number of tokens requested by the merchant. - tokensRequested: number; - - // Number of tokens available to use. - tokensAvailable: number; - - // Legacy compatibility field. Always zero: tokens from another - // merchant are counted as untrusted and cannot be used. - tokensUnexpected: number; - - // Number of tokens not issued by the receiving merchant. - // - // Cannot be used to pay, so an error should be displayed. - tokensUntrusted: number; - - perTokenFamily: { - [slug: string]: { - causeHint?: TokenAvailabilityHint; - requested: number; - available: number; - unexpected: number; - untrusted: number; - }; - }; - } - -.. ts:def:: TokenAvailabilityHint - - type TokenAvailabilityHint = - | "wallet-tokens-available-insufficient" - | "merchant-unexpected" - | "merchant-untrusted"; - -.. ts:def:: PaymentInsufficientBalanceDetails - - // Detailed reason for why the wallet's balance is insufficient. - // - // Current wallet-core versions emit all structured fields. The - // legacy-only alternative lets clients continue decoding responses - // from older cores without allowing partially populated structured - // diagnostics. - type PaymentInsufficientBalanceDetails = - PaymentInsufficientBalanceCompatibilityDetails & - (PaymentInsufficientBalanceStructuredDetails - | PaymentInsufficientBalanceLegacyOnly); - -.. ts:def:: PaymentInsufficientBalanceStructuredDetails - - // Structured explanation emitted by current wallet-core versions. - interface PaymentInsufficientBalanceStructuredDetails { - // Balance in the requested sender scope before payment - // restrictions. - balance: CoinSelectionBalanceSnapshot; - - // Maximum contribution attainable under the failed request's - // actual restrictions and fee policy. For peer payments this is - // the maximum at one exchange, since peer payments cannot combine - // exchanges. - maximumPayableAmount: AmountString; - - // Operation-wide reasons, in deterministic evaluation order. - reasons: CoinSelectionFailureReason[]; - - // Detailed analysis for every same-currency exchange known to - // the wallet. - exchanges: Record<string, CoinSelectionExchangeFailureDiagnostics>; - } - -.. ts:def:: CoinSelectionBalanceSnapshot - - // Balance amounts before age, receiver, wire and fee restrictions. - interface CoinSelectionBalanceSnapshot { - // Balance that the wallet believes it can spend immediately. - material: AmountString; - - // Expected effective output of unfinished refresh operations. - pendingRefresh: AmountString; - - // Material balance plus pending refresh output. - available: AmountString; - } - -.. ts:def:: CoinSelectionExchangeFailureDiagnostics - - interface CoinSelectionExchangeFailureDiagnostics { - // Balance held at this exchange before payment restrictions. - balance: CoinSelectionBalanceSnapshot; - - // Maximum contribution selectable from this exchange for - // this request. - maximumPayableAmount: AmountString; - - // Exchange-local reasons, in deterministic evaluation order. - reasons: CoinSelectionFailureReason[]; - } - -.. ts:def:: PaymentInsufficientBalanceCompatibilityDetails - - // Request context and compatibility fields shared by old and current - // insufficient-balance responses. Fields marked as deprecated are - // compatibility-only and planned for removal after consumers migrate. - interface PaymentInsufficientBalanceCompatibilityDetails { - // Amount requested by the merchant. - amountRequested: AmountString; - - // Wire method for the requested payment, only applicable - // for merchant payments. - wireMethod?: string | undefined; - - // Hint as to why the balance is insufficient. - // - // If this hint is not provided, the balance hints of the - // individual exchanges should be shown, as the overall reason - // might be a combination of the reasons for different exchanges. - // - // Deprecated: use `reasons`. - causeHint?: InsufficientBalanceHint; - - // Balance of type "available". - // Deprecated: use `balance.available`. - balanceAvailable: AmountString; - - // Balance of type "material". - // Deprecated: use `balance.material`. - balanceMaterial: AmountString; - - // Balance of type "age-acceptable". - // Deprecated: use `reasons` and `exchanges`. - balanceAgeAcceptable: AmountString; - - // Balance of type "receiver-acceptable". - // Deprecated: use `reasons` and `exchanges`. - balanceReceiverAcceptable: AmountString; - - // Balance of type "receiver-exchange-url-acceptable". - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverExchangeUrlAcceptable: AmountString; - - // Balance of type "receiver-exchange-pub-acceptable". - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverExchangePubAcceptable: AmountString; - - // Balance of type "receiver-auditor-url-acceptable". - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverAuditorUrlAcceptable: AmountString; - - // Balance of type "merchant-depositable". - // Deprecated: use `maximumPayableAmount` and `reasons`. - balanceReceiverDepositable: AmountString; - - // Deprecated: use `maximumPayableAmount` and `reasons`. - balanceExchangeDepositable: AmountString; - - // Maximum effective amount that the wallet can spend, - // when all fees are paid by the wallet. - // Deprecated: use `maximumPayableAmount`. - maxEffectiveSpendAmount: AmountString; - - // Deprecated: use `exchanges`. - perExchange: { - [url: string]: { - // Deprecated: use `exchanges[url].balance.available`. - balanceAvailable: AmountString; - - // Deprecated: use `exchanges[url].balance.material`. - balanceMaterial: AmountString; - - // Deprecated: use `exchanges[url].maximumPayableAmount` - // and `exchanges[url].reasons`. - balanceExchangeDepositable: AmountString; - - // Deprecated: use `exchanges[url].reasons`. - balanceAgeAcceptable: AmountString; - - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverAcceptable: AmountString; - - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverExchangeUrlAcceptable: AmountString; - - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverExchangePubAcceptable: AmountString; - - // Deprecated: use `exchanges[url].reasons`. - balanceReceiverAuditorUrlAcceptable: AmountString; - - // Deprecated: use `exchanges[url].maximumPayableAmount`. - balanceReceiverDepositable: AmountString; - - // Deprecated: use `exchanges[url].maximumPayableAmount`. - maxEffectiveSpendAmount: AmountString; - - // The exchange master public key configured by the merchant - // backend differs from the one of the coins stored in - // the wallet. - // Deprecated: use the receiver-exchange-master-pub-mismatch - // reason. - exchangeMasterPubMismatch: boolean; - - // Exchange doesn't have global fees configured for the - // relevant year, p2p payments aren't possible. - // Deprecated: use the exchange-global-fees-unavailable reason. - missingGlobalFees: boolean; - - // Hint that UIs should show to explain the insufficient - // balance. - // Deprecated: use `exchanges[url].reasons`. - causeHint?: InsufficientBalanceHint | undefined; - }; - }; - } - -.. ts:def:: PaymentInsufficientBalanceLegacyOnly - - // Alternative emitted by older cores: none of the structured - // fields are present. - interface PaymentInsufficientBalanceLegacyOnly { - balance?: undefined; - maximumPayableAmount?: undefined; - reasons?: undefined; - exchanges?: undefined; - } - -.. ts:def:: CoinSelectionFailureReason - - // Machine-readable reasons that prevented a requested coin - // selection. Unlike `InsufficientBalanceHint`, these values are - // exhaustive and can be reported together. Consumers must branch - // on the discriminator instead of assuming that the first entry - // is the only cause. - type CoinSelectionFailureReason = - | { - type: CoinSelectionFailureReasonType.AvailableBalanceInsufficient; - amountAvailable: AmountString; - } - | { - type: CoinSelectionFailureReasonType.PendingRefresh; - amountPendingRefresh: AmountString; - } - | { - type: CoinSelectionFailureReasonType.MinimumAge; - requiredMinimumAge: number; - amountAgeAcceptable: AmountString; - } - | { - type: CoinSelectionFailureReasonType.ScopeRestricted; - scopeInfo: ScopeInfo; - } - | { type: CoinSelectionFailureReasonType.ReceiverNotAccepted } - | { - type: CoinSelectionFailureReasonType.ReceiverExchangeMasterPubMismatch; - walletMasterPub: string; - receiverMasterPubs: string[]; - } - | { - type: CoinSelectionFailureReasonType.WireMethodUnsupported; - wireMethod: string; - } - | { - type: CoinSelectionFailureReasonType.WireFeeUnavailable; - wireMethod: string; - } - | { - type: CoinSelectionFailureReasonType.DepositAccountRestricted; - wireMethod: string; - accountRestrictions: Record<string, AccountRestriction[]>; - } - | { type: CoinSelectionFailureReasonType.ExchangeGlobalFeesUnavailable } - | { - type: CoinSelectionFailureReasonType.FeesNotCovered; - maximumPayableAmount: AmountString; - } - | { - type: CoinSelectionFailureReasonType.BalanceFragmented; - combinedMaximumPayableAmount: AmountString; - } - | { - type: CoinSelectionFailureReasonType.SupersededExchangeMasterPub; - amountAffected: AmountString; - } - | { type: CoinSelectionFailureReasonType.SelectionFailed }; - -.. ts:def:: CoinSelectionFailureReasonType - - type CoinSelectionFailureReasonType = - | "available-balance-insufficient" - | "pending-refresh" - | "minimum-age" - | "scope-restricted" - | "receiver-not-accepted" - | "receiver-exchange-master-pub-mismatch" - | "wire-method-unsupported" - | "wire-fee-unavailable" - | "deposit-account-restricted" - | "exchange-global-fees-unavailable" - | "fees-not-covered" - | "balance-fragmented" - | "superseded-exchange-master-pub" - | "selection-failed"; - -.. ts:def:: InsufficientBalanceHint - - // Deprecated: use `CoinSelectionFailureReason` instead. - type InsufficientBalanceHint = - // Merchant doesn't accept money from exchange(s) that the - // wallet supports. - | "merchant-accept-insufficient" - // Merchant accepts funds from a matching exchange, but the funds - // can't be deposited with the wire method. - | "merchant-deposit-insufficient" - // While in principle the balance is sufficient, the age - // restriction on coins causes the spendable balance to be - // insufficient. - | "age-restricted" - // Wallet has enough available funds, but the material funds are - // insufficient. Usually because there is a pending refresh - // operation. - | "wallet-balance-material-insufficient" - // The wallet simply doesn't have enough available funds. - | "wallet-balance-available-insufficient" - // Exchange is missing the global fee configuration, thus fees are - // unknown and funds from this exchange can't be used for p2p - // payments. - | "exchange-missing-global-fees" - // Even though the balance looks sufficient for the instructed - // amount, the fees can be covered by neither the merchant nor - // the remaining wallet balance. - | "fees-not-covered"; - - -.. _wallet-op-preparePayForUriV2: - -**preparePayForUriV2** - -Prepare to make a payment based on a ``taler://pay/`` URI. - -Creates a payment transaction for the order identified by the URI, or -reuses the existing transaction if the wallet already knows the order. -The contract terms are then downloaded and processed by the transaction. -Use :ref:`getChoicesForPayment <wallet-op-getChoicesForPayment>` to -inspect the payable choices and :ref:`confirmPay <wallet-op-confirmPay>` -to confirm the payment. - -**Request:** - - The ``args`` must be a `PreparePayRequest` object. - -**Response:** - - On success, the result is a `PreparePayV2Result` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``, ``WALLET_MERCHANT_ORDER_NOT_FOUND``, - ``WALLET_ORDER_ALREADY_CLAIMED``, ``WALLET_ORDER_ALREADY_PAID``, - ``WALLET_CONTRACT_TERMS_MALFORMED``, - ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. - -.. ts:def:: PreparePayRequest - - interface PreparePayRequest { - // The taler://pay/ URI to pay. - talerPayUri: string; - } - -.. ts:def:: PreparePayV2Result - - interface PreparePayV2Result { - // Transaction identifier of the payment transaction. - transactionId: TransactionIdStr; - } - - -.. _wallet-op-preparePayForTemplateV2: - -**preparePayForTemplateV2** - -Prepare to make a payment based on a ``taler://pay-template/`` URI. - -Instantiates the referenced order template at the merchant, honoring -``templateParams`` for template fields that are editable by the -customer, and creates (or reuses) a payment transaction for the -resulting order. As with -:ref:`preparePayForUriV2 <wallet-op-preparePayForUriV2>`, the payment is -then inspected with -:ref:`getChoicesForPayment <wallet-op-getChoicesForPayment>` and -confirmed with :ref:`confirmPay <wallet-op-confirmPay>`. - -**Request:** - - The ``args`` must be a `PreparePayTemplateRequest` object. - -**Response:** - - On success, the result is a `PreparePayV2Result` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``, ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. - -.. ts:def:: PreparePayTemplateRequest - - interface PreparePayTemplateRequest { - // The taler://pay-template/ URI to pay. - talerPayTemplateUri: string; - - // Values for editable template fields. - templateParams?: TemplateParams; - - // Enables progress correlation and cancellation through - // cancelProgressToken. - progressToken?: string; - } - -.. ts:def:: TemplateParams - - type TemplateParams = { - amount?: AmountString; - summary?: string; - }; - - -.. _wallet-op-preparePayForPaivana: - -**preparePayForPaivana** - -Prepare a payment for an HTTP(S) resource protected by a Paivana -paywall. - -The wallet requests the given ``url``, expects an HTTP 402 response that -advertises a payment template in the ``Paivana`` header, instantiates -that template into an order and creates (or reuses) a payment -transaction for it. The returned ``paivana`` redemption metadata must -be kept by the client; once the payment has succeeded, it is passed to -:ref:`getPaivanaCookie <wallet-op-getPaivanaCookie>` to obtain the -access cookie for the resource. - -**Request:** - - The ``args`` must be a `PreparePayForPaivanaRequest` object. - -**Response:** - - On success, the result is a `PreparePayForPaivanaResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_CORE_API_BAD_REQUEST``, ``WALLET_RECEIVED_MALFORMED_RESPONSE``, - ``WALLET_NETWORK_ERROR``, ``WALLET_HTTP_REQUEST_THROTTLED``, - ``WALLET_HTTP_REQUEST_GENERIC_TIMEOUT``, - ``WALLET_UNEXPECTED_REQUEST_ERROR``, - ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. - -.. ts:def:: PreparePayForPaivanaRequest - - interface PreparePayForPaivanaRequest { - // URL of the Paivana-protected resource to pay for. - url: string; - - // Enables progress correlation and cancellation through - // cancelProgressToken. - progressToken?: string; - } - -.. ts:def:: PreparePayForPaivanaResult - - interface PreparePayForPaivanaResult { - // Transaction identifier of the payment transaction. - transactionId: TransactionIdStr; - - paivana: PaivanaRedemption; - } - -.. ts:def:: PaivanaRedemption - - // Information needed to redeem a paid Paivana order for an - // access cookie. - interface PaivanaRedemption { - // Canonical HTTP(S) URL of the protected resource. - url: string; - - // Crockford-base32 encoded 16-byte client nonce. - nonce: string; - - // End of the access period used to derive the Paivana - // session ID. - expiration: TalerProtocolTimestamp; - } - - -.. _wallet-op-getPaivanaCookie: - -**getPaivanaCookie** - -Redeem a successfully paid Paivana transaction for an access cookie. - -The payment transaction referenced by ``transactionId`` must have -completed successfully, and the ``paivana`` redemption metadata must -match the one returned by -:ref:`preparePayForPaivana <wallet-op-preparePayForPaivana>` for that -transaction. The wallet redeems the paid order at the Paivana endpoint -of the protected resource and returns the access cookie; the client can -then request the resource with this cookie. Error codes with the -``PAIVANA_`` prefix are reported by the Paivana server and forwarded by -the wallet. - -**Request:** - - The ``args`` must be a `GetPaivanaCookieRequest` object. - -**Response:** - - On success, the result is a `GetPaivanaCookieResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_CORE_API_BAD_REQUEST``, ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, - ``WALLET_RECEIVED_MALFORMED_RESPONSE``, ``WALLET_NETWORK_ERROR``, - ``WALLET_HTTP_REQUEST_THROTTLED``, - ``WALLET_HTTP_REQUEST_GENERIC_TIMEOUT``, ``PAIVANA_PAYMENT_MISSING``, - ``PAIVANA_BACKEND_REFUSED``, ``PAIVANA_ORDER_UNKNOWN``, - ``PAIVANA_BACKEND_ERROR``, ``PAIVANA_GET_ORDER_FAILED``, - ``PAIVANA_WRONG_ORDER``, ``PAIVANA_TOO_LATE``, - ``PAIVANA_INVALID_TARGET``. - -.. ts:def:: GetPaivanaCookieRequest - - interface GetPaivanaCookieRequest { - // Transaction identifier of the paid Paivana payment. - transactionId: TransactionIdStr; - - paivana: PaivanaRedemption; - } - -.. ts:def:: GetPaivanaCookieResult - - interface GetPaivanaCookieResult { - // Plain Cookie request-header value, without Set-Cookie - // attributes. - cookie: string; - } - - -.. _wallet-op-unclaimPayment: - -**unclaimPayment** - -Release this wallet's claim on an unpaid order, so that the payment can -be handed off to another wallet. - -The merchant is asked to release the order, and the operation returns a -public ``taler://pay/`` URI for the order that contains no private -nonce. Another wallet can use this URI with -:ref:`preparePayForUriV2 <wallet-op-preparePayForUriV2>` to claim and -pay the order instead. The payment can only be released before it is -paid; use :ref:`reclaimPayment <wallet-op-reclaimPayment>` to claim the -order again for this wallet. - -**Request:** - - The ``args`` must be an `UnclaimPaymentRequest` object. - -**Response:** - - On success, the result is an `UnclaimPaymentResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, - ``WALLET_CORE_REQUEST_CANCELLED``, - ``WALLET_UNEXPECTED_REQUEST_ERROR``. - -.. ts:def:: UnclaimPaymentRequest - - interface UnclaimPaymentRequest { - // Transaction identifier of the payment to release. - transactionId: TransactionIdStr; - - // Enables progress correlation and cancellation through - // cancelProgressToken. - progressToken?: string; - } - -.. ts:def:: UnclaimPaymentResult - - interface UnclaimPaymentResult { - // Public taler://pay/ URI that another wallet can claim. - talerPayUri: TalerUriString; - } - - -.. _wallet-op-reclaimPayment: - -**reclaimPayment** - -Claim an order again for this wallet after it was released for handoff -with :ref:`unclaimPayment <wallet-op-unclaimPayment>`. Claiming again -with the same nonce is idempotent. - -**Request:** - - The ``args`` must be a `ReclaimPaymentRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. - -.. ts:def:: ReclaimPaymentRequest - - interface ReclaimPaymentRequest { - // Transaction identifier of the payment to claim again. - transactionId: TransactionIdStr; - } - - -.. _wallet-op-checkPayForTemplate: - -**checkPayForTemplate** - -Check a ``taler://pay-template/`` URI without creating a payment -transaction. - -Fetches the template details from the merchant, applies the overrides -encoded in the URI (such as amount or summary), and returns the template -details together with the currencies supported by the merchant. This -allows the client to present the editable template fields to the user -before calling -:ref:`preparePayForTemplateV2 <wallet-op-preparePayForTemplateV2>`. - -**Request:** - - The ``args`` must be a `CheckPayTemplateRequest` object. - -**Response:** - - On success, the result is a `CheckPayTemplateReponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``, ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. - -**Details:** - - The ``templateDetails`` field is the template information served by - the merchant backend (a `WalletTemplateDetailsResponse` of the - merchant API), with the URI overrides already applied. - -.. ts:def:: CheckPayTemplateRequest - - interface CheckPayTemplateRequest { - // The taler://pay-template/ URI to check. - talerPayTemplateUri: string; - - // Enables progress correlation and cancellation through - // cancelProgressToken. - progressToken?: string; - } - -.. ts:def:: CheckPayTemplateReponse - - type CheckPayTemplateReponse = { - // Template details served by the merchant backend. - templateDetails: WalletTemplateDetailsResponse; - - // Currencies supported by the merchant, sorted. - supportedCurrencies: string[]; - }; - - -.. _wallet-op-startRefundQueryForUri: - -**startRefundQueryForUri** - -Check for a refund based on a ``taler://refund`` URI. - -Locates the payment for the order identified by the URI and starts a -refund query on it; the refund processing then continues as part of the -payment transaction. The -``WALLET_PURCHASE_NOT_FOUND`` error is returned when the wallet has no -purchase for the order, for example because it was paid with a -different wallet. - -**Request:** - - The ``args`` must be a `PrepareRefundRequest` object. - -**Response:** - - On success, the result is a `StartRefundQueryForUriResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``, ``WALLET_PURCHASE_NOT_FOUND``. - -.. ts:def:: PrepareRefundRequest - - interface PrepareRefundRequest { - // The taler://refund URI to check for refunds. - talerRefundUri: string; - } - -.. ts:def:: StartRefundQueryForUriResponse - - interface StartRefundQueryForUriResponse { - // Transaction id of the *payment* where the refund query - // was started. - transactionId: TransactionIdStr; - } - - -.. _wallet-op-startRefundQuery: - -**startRefundQuery** - -Start a refund query for an existing payment transaction, referenced by -its transaction identifier. Only payments that completed successfully -are queried; for payments in any other state, the request has no -effect. - -**Request:** - - The ``args`` must be a `StartRefundQueryRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: StartRefundQueryRequest - - interface StartRefundQueryRequest { - // Transaction identifier of the payment to query refunds for. - transactionId: TransactionIdStr; - } - - -.. _wallet-op-confirmPay: - -**confirmPay** - -Confirm a payment that was previously prepared with -:ref:`preparePayForUriV2 <wallet-op-preparePayForUriV2>`, -:ref:`preparePayForTemplateV2 <wallet-op-preparePayForTemplateV2>` or -:ref:`preparePayForPaivana <wallet-op-preparePayForPaivana>`. - -The wallet selects the coins (and, for a contract v1 order, the tokens) -for the payment and submits it to the merchant. For a contract v1 -order, ``choiceIndex`` must refer to one of the choices returned by -:ref:`getChoicesForPayment <wallet-op-getChoicesForPayment>`. - -**Request:** - - The ``args`` must be a `ConfirmPayRequest` object. - -**Response:** - - On success, the result is a `ConfirmPayResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE``, - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, - ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - Unless ``noWait`` is set, the operation waits for the first payment - result: when the payment succeeded, the result has type ``done``; when - the payment could not be completed (yet), the result has type - ``pending`` and carries the last error of the payment. With - ``noWait``, the operation returns a ``pending`` result immediately - and the payment status is communicated via notifications. - - The ``WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE`` error carries - ``insufficientBalanceDetails`` of type - `PaymentInsufficientBalanceDetails` in its error detail. - -.. ts:def:: ConfirmPayRequest - - interface ConfirmPayRequest { - // Transaction identifier of the prepared payment. - transactionId: TransactionIdStr; - - // Request that a donation receipt for this payment is collected - // via the configured donau, if the selected choice offers a - // matching tax-receipt output. - useDonau?: boolean; - - // Session ID override for the payment. - sessionId?: string; - - // Currently ignored by wallet-core. - forcedCoinSel?: ForcedCoinSel; - - // Legacy compatibility option for v1 orders. Ignored: tokens can - // only be spent at their issuing merchant, even when this is true. - forcedTokenSel?: boolean; - - // Only applies to v1 orders. - choiceIndex?: number; - - // Do not wait for the first payment success or error - // before returning a response. Instead, status will - // be communicated via notifications. - // - // Will become the default in future versions. - noWait?: boolean; - } - -.. ts:def:: ConfirmPayResult - - // Result for confirmPay. - type ConfirmPayResult = ConfirmPayResultDone | ConfirmPayResultPending; - -.. ts:def:: ConfirmPayResultDone - - interface ConfirmPayResultDone { - type: ConfirmPayResultType.Done; - contractTerms: MerchantContractTermsV0; - transactionId: TransactionIdStr; - } - -.. ts:def:: ConfirmPayResultPending - - interface ConfirmPayResultPending { - type: ConfirmPayResultType.Pending; - transactionId: TransactionIdStr; - lastError?: TalerErrorDetail | undefined; - } - -.. ts:def:: ConfirmPayResultType - - type ConfirmPayResultType = "done" | "pending"; diff --git a/core/wallet-core/payments/check-pay-for-template.rst b/core/wallet-core/payments/check-pay-for-template.rst @@ -0,0 +1,56 @@ +.. ts:op:: checkPayForTemplate + + Check a ``taler://pay-template/`` URI without creating a payment + transaction. + + Fetches the template details from the merchant, applies the overrides + encoded in the URI (such as amount or summary), and returns the + template details together with the currencies supported by the + merchant. This allows the client to present the editable template + fields to the user before calling :ts:op:`preparePayForTemplateV2`. + + **Request:** + + The ``args`` must be a `CheckPayTemplateRequest` object. + + **Response:** + + On success, the result is a `CheckPayTemplateReponse` object. + + **Side effects:** + + Fetches the payment template and the merchant's configuration over + the network; does not change wallet state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``, + ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. + + **Details:** + + The ``templateDetails`` field is the template information served + by the merchant backend (a `WalletTemplateDetailsResponse` of the + merchant API), with the URI overrides already applied. + +.. ts:def:: CheckPayTemplateRequest + + interface CheckPayTemplateRequest { + // The taler://pay-template/ URI to check. + talerPayTemplateUri: string; + + // Enables progress correlation and cancellation through + // cancelProgressToken. + progressToken?: string; + } + +.. ts:def:: CheckPayTemplateReponse + + type CheckPayTemplateReponse = { + // Template details served by the merchant backend. + templateDetails: WalletTemplateDetailsResponse; + + // Currencies supported by the merchant, sorted. + supportedCurrencies: string[]; + }; diff --git a/core/wallet-core/payments/confirm-pay.rst b/core/wallet-core/payments/confirm-pay.rst @@ -0,0 +1,398 @@ +.. ts:op:: confirmPay + + Confirm a payment that was previously prepared with + :ts:op:`preparePayForUriV2`, :ts:op:`preparePayForTemplateV2` or + :ts:op:`preparePayForPaivana`. + + The wallet selects the coins (and, for a contract v1 order, the + tokens) for the payment and submits it to the merchant. For a + contract v1 order, ``choiceIndex`` must refer to one of the choices + returned by :ts:op:`getChoicesForPayment`. + + **Request:** + + The ``args`` must be a `ConfirmPayRequest` object. + + **Response:** + + On success, the result is a `ConfirmPayResult` object. + + **Side effects:** + + Confirms the prepared payment: the wallet spends the coins and + submits the payment to the merchant over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE``, + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, + ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + Unless ``noWait`` is set, the operation waits for the first + payment result: when the payment succeeded, the result has type + ``done``; when the payment could not be completed (yet), the + result has type ``pending`` and carries the last error of the + payment. With ``noWait``, the operation returns a ``pending`` + result immediately and the payment status is communicated via + notifications. + + The ``WALLET_PAY_MERCHANT_INSUFFICIENT_BALANCE`` error carries + ``insufficientBalanceDetails`` of type + `PaymentInsufficientBalanceDetails` in its error detail. + +.. ts:def:: PaymentInsufficientBalanceDetails + + // Detailed reason for why the wallet's balance is insufficient. + // + // Current wallet-core versions emit all structured fields. The + // legacy-only alternative lets clients continue decoding responses + // from older cores without allowing partially populated structured + // diagnostics. + type PaymentInsufficientBalanceDetails = + PaymentInsufficientBalanceCompatibilityDetails & + (PaymentInsufficientBalanceStructuredDetails + | PaymentInsufficientBalanceLegacyOnly); + +.. ts:def:: PaymentInsufficientBalanceStructuredDetails + + // Structured explanation emitted by current wallet-core versions. + interface PaymentInsufficientBalanceStructuredDetails { + // Balance in the requested sender scope before payment + // restrictions. + balance: CoinSelectionBalanceSnapshot; + + // Maximum contribution attainable under the failed request's + // actual restrictions and fee policy. For peer payments this is + // the maximum at one exchange, since peer payments cannot combine + // exchanges. + maximumPayableAmount: AmountString; + + // Operation-wide reasons, in deterministic evaluation order. + reasons: CoinSelectionFailureReason[]; + + // Detailed analysis for every same-currency exchange known to + // the wallet. + exchanges: Record<string, CoinSelectionExchangeFailureDiagnostics>; + } + +.. ts:def:: CoinSelectionBalanceSnapshot + + // Balance amounts before age, receiver, wire and fee restrictions. + interface CoinSelectionBalanceSnapshot { + // Balance that the wallet believes it can spend immediately. + material: AmountString; + + // Expected effective output of unfinished refresh operations. + pendingRefresh: AmountString; + + // Material balance plus pending refresh output. + available: AmountString; + } + +.. ts:def:: CoinSelectionExchangeFailureDiagnostics + + interface CoinSelectionExchangeFailureDiagnostics { + // Balance held at this exchange before payment restrictions. + balance: CoinSelectionBalanceSnapshot; + + // Maximum contribution selectable from this exchange for + // this request. + maximumPayableAmount: AmountString; + + // Exchange-local reasons, in deterministic evaluation order. + reasons: CoinSelectionFailureReason[]; + } + +.. ts:def:: PaymentInsufficientBalanceCompatibilityDetails + + // Request context and compatibility fields shared by old and current + // insufficient-balance responses. Fields marked as deprecated are + // compatibility-only and planned for removal after consumers migrate. + interface PaymentInsufficientBalanceCompatibilityDetails { + // Amount requested by the merchant. + amountRequested: AmountString; + + // Wire method for the requested payment, only applicable + // for merchant payments. + wireMethod?: string | undefined; + + // Hint as to why the balance is insufficient. + // + // If this hint is not provided, the balance hints of the + // individual exchanges should be shown, as the overall reason + // might be a combination of the reasons for different exchanges. + // + // Deprecated: use `reasons`. + causeHint?: InsufficientBalanceHint; + + // Balance of type "available". + // Deprecated: use `balance.available`. + balanceAvailable: AmountString; + + // Balance of type "material". + // Deprecated: use `balance.material`. + balanceMaterial: AmountString; + + // Balance of type "age-acceptable". + // Deprecated: use `reasons` and `exchanges`. + balanceAgeAcceptable: AmountString; + + // Balance of type "receiver-acceptable". + // Deprecated: use `reasons` and `exchanges`. + balanceReceiverAcceptable: AmountString; + + // Balance of type "receiver-exchange-url-acceptable". + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverExchangeUrlAcceptable: AmountString; + + // Balance of type "receiver-exchange-pub-acceptable". + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverExchangePubAcceptable: AmountString; + + // Balance of type "receiver-auditor-url-acceptable". + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverAuditorUrlAcceptable: AmountString; + + // Balance of type "merchant-depositable". + // Deprecated: use `maximumPayableAmount` and `reasons`. + balanceReceiverDepositable: AmountString; + + // Deprecated: use `maximumPayableAmount` and `reasons`. + balanceExchangeDepositable: AmountString; + + // Maximum effective amount that the wallet can spend, + // when all fees are paid by the wallet. + // Deprecated: use `maximumPayableAmount`. + maxEffectiveSpendAmount: AmountString; + + // Deprecated: use `exchanges`. + perExchange: { + [url: string]: { + // Deprecated: use `exchanges[url].balance.available`. + balanceAvailable: AmountString; + + // Deprecated: use `exchanges[url].balance.material`. + balanceMaterial: AmountString; + + // Deprecated: use `exchanges[url].maximumPayableAmount` + // and `exchanges[url].reasons`. + balanceExchangeDepositable: AmountString; + + // Deprecated: use `exchanges[url].reasons`. + balanceAgeAcceptable: AmountString; + + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverAcceptable: AmountString; + + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverExchangeUrlAcceptable: AmountString; + + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverExchangePubAcceptable: AmountString; + + // Deprecated: use `exchanges[url].reasons`. + balanceReceiverAuditorUrlAcceptable: AmountString; + + // Deprecated: use `exchanges[url].maximumPayableAmount`. + balanceReceiverDepositable: AmountString; + + // Deprecated: use `exchanges[url].maximumPayableAmount`. + maxEffectiveSpendAmount: AmountString; + + // The exchange master public key configured by the merchant + // backend differs from the one of the coins stored in + // the wallet. + // Deprecated: use the receiver-exchange-master-pub-mismatch + // reason. + exchangeMasterPubMismatch: boolean; + + // Exchange doesn't have global fees configured for the + // relevant year, p2p payments aren't possible. + // Deprecated: use the exchange-global-fees-unavailable reason. + missingGlobalFees: boolean; + + // Hint that UIs should show to explain the insufficient + // balance. + // Deprecated: use `exchanges[url].reasons`. + causeHint?: InsufficientBalanceHint | undefined; + }; + }; + } + +.. ts:def:: PaymentInsufficientBalanceLegacyOnly + + // Alternative emitted by older cores: none of the structured + // fields are present. + interface PaymentInsufficientBalanceLegacyOnly { + balance?: undefined; + maximumPayableAmount?: undefined; + reasons?: undefined; + exchanges?: undefined; + } + +.. ts:def:: CoinSelectionFailureReason + + // Machine-readable reasons that prevented a requested coin + // selection. Unlike `InsufficientBalanceHint`, these values are + // exhaustive and can be reported together. Consumers must branch + // on the discriminator instead of assuming that the first entry + // is the only cause. + type CoinSelectionFailureReason = + | { + type: CoinSelectionFailureReasonType.AvailableBalanceInsufficient; + amountAvailable: AmountString; + } + | { + type: CoinSelectionFailureReasonType.PendingRefresh; + amountPendingRefresh: AmountString; + } + | { + type: CoinSelectionFailureReasonType.MinimumAge; + requiredMinimumAge: number; + amountAgeAcceptable: AmountString; + } + | { + type: CoinSelectionFailureReasonType.ScopeRestricted; + scopeInfo: ScopeInfo; + } + | { type: CoinSelectionFailureReasonType.ReceiverNotAccepted } + | { + type: CoinSelectionFailureReasonType.ReceiverExchangeMasterPubMismatch; + walletMasterPub: string; + receiverMasterPubs: string[]; + } + | { + type: CoinSelectionFailureReasonType.WireMethodUnsupported; + wireMethod: string; + } + | { + type: CoinSelectionFailureReasonType.WireFeeUnavailable; + wireMethod: string; + } + | { + type: CoinSelectionFailureReasonType.DepositAccountRestricted; + wireMethod: string; + accountRestrictions: Record<string, AccountRestriction[]>; + } + | { type: CoinSelectionFailureReasonType.ExchangeGlobalFeesUnavailable } + | { + type: CoinSelectionFailureReasonType.FeesNotCovered; + maximumPayableAmount: AmountString; + } + | { + type: CoinSelectionFailureReasonType.BalanceFragmented; + combinedMaximumPayableAmount: AmountString; + } + | { + type: CoinSelectionFailureReasonType.SupersededExchangeMasterPub; + amountAffected: AmountString; + } + | { type: CoinSelectionFailureReasonType.SelectionFailed }; + +.. ts:def:: CoinSelectionFailureReasonType + + type CoinSelectionFailureReasonType = + | "available-balance-insufficient" + | "pending-refresh" + | "minimum-age" + | "scope-restricted" + | "receiver-not-accepted" + | "receiver-exchange-master-pub-mismatch" + | "wire-method-unsupported" + | "wire-fee-unavailable" + | "deposit-account-restricted" + | "exchange-global-fees-unavailable" + | "fees-not-covered" + | "balance-fragmented" + | "superseded-exchange-master-pub" + | "selection-failed"; + +.. ts:def:: InsufficientBalanceHint + + // Deprecated: use `CoinSelectionFailureReason` instead. + type InsufficientBalanceHint = + // Merchant doesn't accept money from exchange(s) that the + // wallet supports. + | "merchant-accept-insufficient" + // Merchant accepts funds from a matching exchange, but the funds + // can't be deposited with the wire method. + | "merchant-deposit-insufficient" + // While in principle the balance is sufficient, the age + // restriction on coins causes the spendable balance to be + // insufficient. + | "age-restricted" + // Wallet has enough available funds, but the material funds are + // insufficient. Usually because there is a pending refresh + // operation. + | "wallet-balance-material-insufficient" + // The wallet simply doesn't have enough available funds. + | "wallet-balance-available-insufficient" + // Exchange is missing the global fee configuration, thus fees are + // unknown and funds from this exchange can't be used for p2p + // payments. + | "exchange-missing-global-fees" + // Even though the balance looks sufficient for the instructed + // amount, the fees can be covered by neither the merchant nor + // the remaining wallet balance. + | "fees-not-covered"; + +.. ts:def:: ConfirmPayRequest + + interface ConfirmPayRequest { + // Transaction identifier of the prepared payment. + transactionId: TransactionIdStr; + + // Request that a donation receipt for this payment is collected + // via the configured donau, if the selected choice offers a + // matching tax-receipt output. + useDonau?: boolean; + + // Session ID override for the payment. + sessionId?: string; + + // Currently ignored by wallet-core. + forcedCoinSel?: ForcedCoinSel; + + // Legacy compatibility option for v1 orders. Ignored: tokens can + // only be spent at their issuing merchant, even when this is true. + forcedTokenSel?: boolean; + + // Only applies to v1 orders. + choiceIndex?: number; + + // Do not wait for the first payment success or error + // before returning a response. Instead, status will + // be communicated via notifications. + // + // Will become the default in future versions. + noWait?: boolean; + } + +.. ts:def:: ConfirmPayResult + + // Result for confirmPay. + type ConfirmPayResult = ConfirmPayResultDone | ConfirmPayResultPending; + +.. ts:def:: ConfirmPayResultDone + + interface ConfirmPayResultDone { + type: ConfirmPayResultType.Done; + contractTerms: MerchantContractTermsV0; + transactionId: TransactionIdStr; + } + +.. ts:def:: ConfirmPayResultPending + + interface ConfirmPayResultPending { + type: ConfirmPayResultType.Pending; + transactionId: TransactionIdStr; + lastError?: TalerErrorDetail | undefined; + } + +.. ts:def:: ConfirmPayResultType + + type ConfirmPayResultType = "done" | "pending"; diff --git a/core/wallet-core/payments/get-choices-for-payment.rst b/core/wallet-core/payments/get-choices-for-payment.rst @@ -0,0 +1,173 @@ +.. ts:op:: getChoicesForPayment + + Get the list of contract choices for a payment transaction in the + dialog (confirmation) state, together with information on whether each + choice can be paid with the funds available in the wallet, and whether + a specific choice should be paid automatically without user + confirmation, based on the user's configuration or the type of payment + requested. + + For a contract v1 order, the ``choices`` array of the result mirrors + the choices of the contract. For a contract v0 order, which has no + choices, it contains a single choice with no inputs/outputs. + + **Request:** + + The ``args`` must be a `GetChoicesForPaymentRequest` object. + + **Response:** + + On success, the result is a `GetChoicesForPaymentResult` object. + + **Side effects:** + + This operation is normally a pure read, but it may refresh outdated + exchange key material over the network and write denomination + verification results to the wallet database. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, + ``WALLET_TRANSACTION_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. + + The ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED`` error is + returned while the contract terms of the payment have not been + downloaded yet; the caller should wait for the corresponding + transaction state transition and try again. + +.. ts:def:: GetChoicesForPaymentRequest + + interface GetChoicesForPaymentRequest { + // Transaction identifier of the payment. + transactionId: string; + + // Force a particular coin selection when evaluating + // whether the choices are payable. + forcedCoinSel?: ForcedCoinSel; + } + +.. ts:def:: ForcedCoinSel + + interface ForcedCoinSel { + coins: { + value: AmountString; + contribution: AmountString; + }[]; + } + +.. ts:def:: GetChoicesForPaymentResult + + type GetChoicesForPaymentResult = { + // Details for all choices in the contract. + // + // The index in this array corresponds to the choice + // index in the original contract v1. For contract v0 + // orders, it will only contain a single choice with no + // inputs/outputs. + choices: ChoiceSelectionDetail[]; + + // Index of the choice in the `choices` array to present + // to the user as default. + // + // Won't be set if no default selection is configured + // or no choice is payable; otherwise it will always + // be 0 for v0 orders. + defaultChoiceIndex?: number; + + // Whether the choice referenced by `automaticExecutableIndex` + // should be confirmed automatically without user interaction. + // + // If true, the wallet should call `confirmPay` immediately + // afterwards; if false, the user should first be prompted + // to select and confirm a choice. Undefined when no + // choices are payable. + automaticExecution?: boolean; + + // Index of the choice that would be set to automatically + // execute if the choice was payable. When `automaticExecution` + // is set to true, the payment should be confirmed with this + // choice index without user interaction. + automaticExecutableIndex?: number; + + // Data extracted from the contract terms that + // is relevant for payment processing in the wallet. + contractTerms: MerchantContractTerms; + }; + +.. ts:def:: ChoiceSelectionDetail + + type ChoiceSelectionDetail = + | ChoiceSelectionDetailPaymentPossible + | ChoiceSelectionDetailInsufficientBalance; + +.. ts:def:: ChoiceSelectionDetailPaymentPossible + + interface ChoiceSelectionDetailPaymentPossible { + status: ChoiceSelectionDetailType.PaymentPossible; + + // Amount requested by the contract for this choice. + amountRaw: AmountString; + + // Total cost of this choice for the wallet, including fees. + amountEffective: AmountString; + + // Scope of the coins that would be spent, if known. + scopeInfo: ScopeInfo | undefined; + + tokenDetails?: PaymentTokenAvailabilityDetails; + } + +.. ts:def:: ChoiceSelectionDetailInsufficientBalance + + interface ChoiceSelectionDetailInsufficientBalance { + status: ChoiceSelectionDetailType.InsufficientBalance; + + // Amount requested by the contract for this choice. + amountRaw: AmountString; + + balanceDetails?: PaymentInsufficientBalanceDetails; + tokenDetails?: PaymentTokenAvailabilityDetails; + } + +.. ts:def:: ChoiceSelectionDetailType + + type ChoiceSelectionDetailType = + | "payment-possible" + | "insufficient-balance"; + +.. ts:def:: PaymentTokenAvailabilityDetails + + interface PaymentTokenAvailabilityDetails { + // Number of tokens requested by the merchant. + tokensRequested: number; + + // Number of tokens available to use. + tokensAvailable: number; + + // Legacy compatibility field. Always zero: tokens from another + // merchant are counted as untrusted and cannot be used. + tokensUnexpected: number; + + // Number of tokens not issued by the receiving merchant. + // + // Cannot be used to pay, so an error should be displayed. + tokensUntrusted: number; + + perTokenFamily: { + [slug: string]: { + causeHint?: TokenAvailabilityHint; + requested: number; + available: number; + unexpected: number; + untrusted: number; + }; + }; + } + +.. ts:def:: TokenAvailabilityHint + + type TokenAvailabilityHint = + | "wallet-tokens-available-insufficient" + | "merchant-unexpected" + | "merchant-untrusted"; diff --git a/core/wallet-core/payments/get-paivana-cookie.rst b/core/wallet-core/payments/get-paivana-cookie.rst @@ -0,0 +1,55 @@ +.. ts:op:: getPaivanaCookie + + Redeem a successfully paid Paivana transaction for an access cookie. + + The payment transaction referenced by ``transactionId`` must have + completed successfully, and the ``paivana`` redemption metadata must + match the one returned by :ts:op:`preparePayForPaivana` for that + transaction. The wallet redeems the paid order at the Paivana + endpoint of the protected resource and returns the access cookie; the + client can then request the resource with this cookie. Error codes + with the ``PAIVANA_`` prefix are reported by the Paivana server and + forwarded by the wallet. + + **Request:** + + The ``args`` must be a `GetPaivanaCookieRequest` object. + + **Response:** + + On success, the result is a `GetPaivanaCookieResult` object. + + **Side effects:** + + Redeems the paid Paivana transaction for an access cookie via an + HTTP POST to the resource server. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_CORE_API_BAD_REQUEST``, ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, + ``WALLET_RECEIVED_MALFORMED_RESPONSE``, ``WALLET_NETWORK_ERROR``, + ``WALLET_HTTP_REQUEST_THROTTLED``, + ``WALLET_HTTP_REQUEST_GENERIC_TIMEOUT``, ``PAIVANA_PAYMENT_MISSING``, + ``PAIVANA_BACKEND_REFUSED``, ``PAIVANA_ORDER_UNKNOWN``, + ``PAIVANA_BACKEND_ERROR``, ``PAIVANA_GET_ORDER_FAILED``, + ``PAIVANA_WRONG_ORDER``, ``PAIVANA_TOO_LATE``, + ``PAIVANA_INVALID_TARGET``. + +.. ts:def:: GetPaivanaCookieRequest + + interface GetPaivanaCookieRequest { + // Transaction identifier of the paid Paivana payment. + transactionId: TransactionIdStr; + + paivana: PaivanaRedemption; + } + +.. ts:def:: GetPaivanaCookieResult + + interface GetPaivanaCookieResult { + // Plain Cookie request-header value, without Set-Cookie + // attributes. + cookie: string; + } diff --git a/core/wallet-core/payments/prepare-pay-for-paivana.rst b/core/wallet-core/payments/prepare-pay-for-paivana.rst @@ -0,0 +1,71 @@ +.. ts:op:: preparePayForPaivana + + Prepare a payment for an HTTP(S) resource protected by a Paivana + paywall. + + The wallet requests the given ``url``, expects an HTTP 402 response + that advertises a payment template in the ``Paivana`` header, + instantiates that template into an order and creates (or reuses) a + payment transaction for it. The returned ``paivana`` redemption + metadata must be kept by the client; once the payment has succeeded, + it is passed to :ts:op:`getPaivanaCookie` to obtain the access cookie + for the resource. + + **Request:** + + The ``args`` must be a `PreparePayForPaivanaRequest` object. + + **Response:** + + On success, the result is a `PreparePayForPaivanaResult` object. + + **Side effects:** + + Fetches the contract terms for the Paivana-protected resource over + the network and creates a payment transaction in dialog state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_CORE_API_BAD_REQUEST``, + ``WALLET_RECEIVED_MALFORMED_RESPONSE``, ``WALLET_NETWORK_ERROR``, + ``WALLET_HTTP_REQUEST_THROTTLED``, + ``WALLET_HTTP_REQUEST_GENERIC_TIMEOUT``, + ``WALLET_UNEXPECTED_REQUEST_ERROR``, + ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. + +.. ts:def:: PreparePayForPaivanaRequest + + interface PreparePayForPaivanaRequest { + // URL of the Paivana-protected resource to pay for. + url: string; + + // Enables progress correlation and cancellation through + // cancelProgressToken. + progressToken?: string; + } + +.. ts:def:: PreparePayForPaivanaResult + + interface PreparePayForPaivanaResult { + // Transaction identifier of the payment transaction. + transactionId: TransactionIdStr; + + paivana: PaivanaRedemption; + } + +.. ts:def:: PaivanaRedemption + + // Information needed to redeem a paid Paivana order for an + // access cookie. + interface PaivanaRedemption { + // Canonical HTTP(S) URL of the protected resource. + url: string; + + // Crockford-base32 encoded 16-byte client nonce. + nonce: string; + + // End of the access period used to derive the Paivana + // session ID. + expiration: TalerProtocolTimestamp; + } diff --git a/core/wallet-core/payments/prepare-pay-for-template-v2.rst b/core/wallet-core/payments/prepare-pay-for-template-v2.rst @@ -0,0 +1,50 @@ +.. ts:op:: preparePayForTemplateV2 + + Prepare to make a payment based on a ``taler://pay-template/`` URI. + + Instantiates the referenced order template at the merchant, honoring + ``templateParams`` for template fields that are editable by the + customer, and creates (or reuses) a payment transaction for the + resulting order. As with :ts:op:`preparePayForUriV2`, the payment is + then inspected with :ts:op:`getChoicesForPayment` and confirmed with + :ts:op:`confirmPay`. + + **Request:** + + The ``args`` must be a `PreparePayTemplateRequest` object. + + **Response:** + + On success, the result is a `PreparePayV2Result` object. + + **Side effects:** + + Fetches the payment template from the merchant over the network + and creates a payment transaction in dialog state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``, + ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. + +.. ts:def:: PreparePayTemplateRequest + + interface PreparePayTemplateRequest { + // The taler://pay-template/ URI to pay. + talerPayTemplateUri: string; + + // Values for editable template fields. + templateParams?: TemplateParams; + + // Enables progress correlation and cancellation through + // cancelProgressToken. + progressToken?: string; + } + +.. ts:def:: TemplateParams + + type TemplateParams = { + amount?: AmountString; + summary?: string; + }; diff --git a/core/wallet-core/payments/prepare-pay-for-uri-v2.rst b/core/wallet-core/payments/prepare-pay-for-uri-v2.rst @@ -0,0 +1,45 @@ +.. ts:op:: preparePayForUriV2 + + Prepare to make a payment based on a ``taler://pay/`` URI. + + Creates a payment transaction for the order identified by the URI, or + reuses the existing transaction if the wallet already knows the order. + The contract terms are then downloaded and processed by the + transaction. Use :ts:op:`getChoicesForPayment` to inspect the payable + choices and :ts:op:`confirmPay` to confirm the payment. + + **Request:** + + The ``args`` must be a `PreparePayRequest` object. + + **Response:** + + On success, the result is a `PreparePayV2Result` object. + + **Side effects:** + + Fetches the order and contract terms from the merchant over the + network, claims the order, and creates a payment transaction in + dialog state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``, ``WALLET_MERCHANT_ORDER_NOT_FOUND``, + ``WALLET_ORDER_ALREADY_CLAIMED``, ``WALLET_ORDER_ALREADY_PAID``, + ``WALLET_CONTRACT_TERMS_MALFORMED``, + ``WALLET_CONTRACT_TERMS_UNSUPPORTED``. + +.. ts:def:: PreparePayRequest + + interface PreparePayRequest { + // The taler://pay/ URI to pay. + talerPayUri: string; + } + +.. ts:def:: PreparePayV2Result + + interface PreparePayV2Result { + // Transaction identifier of the payment transaction. + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/payments/reclaim-payment.rst b/core/wallet-core/payments/reclaim-payment.rst @@ -0,0 +1,31 @@ +.. ts:op:: reclaimPayment + + Claim an order again for this wallet after it was released for + handoff with :ts:op:`unclaimPayment`. Claiming again with the same + nonce is idempotent. + + **Request:** + + The ``args`` must be a `ReclaimPaymentRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Reclaims a payment previously handed off to another wallet; + changes the transaction state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. + +.. ts:def:: ReclaimPaymentRequest + + interface ReclaimPaymentRequest { + // Transaction identifier of the payment to claim again. + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/payments/start-refund-query-for-uri.rst b/core/wallet-core/payments/start-refund-query-for-uri.rst @@ -0,0 +1,43 @@ +.. ts:op:: startRefundQueryForUri + + Check for a refund based on a ``taler://refund`` URI. + + Locates the payment for the order identified by the URI and starts a + refund query on it; the refund processing then continues as part of + the payment transaction. The ``WALLET_PURCHASE_NOT_FOUND`` error is + returned when the wallet has no purchase for the order, for example + because it was paid with a different wallet. + + **Request:** + + The ``args`` must be a `PrepareRefundRequest` object. + + **Response:** + + On success, the result is a `StartRefundQueryForUriResponse` + object. + + **Side effects:** + + Creates or updates a refund query and contacts the merchant over + the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``, ``WALLET_PURCHASE_NOT_FOUND``. + +.. ts:def:: PrepareRefundRequest + + interface PrepareRefundRequest { + // The taler://refund URI to check for refunds. + talerRefundUri: string; + } + +.. ts:def:: StartRefundQueryForUriResponse + + interface StartRefundQueryForUriResponse { + // Transaction id of the *payment* where the refund query + // was started. + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/payments/start-refund-query.rst b/core/wallet-core/payments/start-refund-query.rst @@ -0,0 +1,26 @@ +.. ts:op:: startRefundQuery + + Start a refund query for an existing payment transaction, referenced + by its transaction identifier. Only payments that completed + successfully are queried; for payments in any other state, the + request has no effect. + + **Request:** + + The ``args`` must be a `StartRefundQueryRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Starts a refund query for an existing payment transaction, + contacting the merchant over the network. + +.. ts:def:: StartRefundQueryRequest + + interface StartRefundQueryRequest { + // Transaction identifier of the payment to query refunds for. + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/payments/unclaim-payment.rst b/core/wallet-core/payments/unclaim-payment.rst @@ -0,0 +1,51 @@ +.. ts:op:: unclaimPayment + + Release this wallet's claim on an unpaid order, so that the payment + can be handed off to another wallet. + + The merchant is asked to release the order, and the operation returns + a public ``taler://pay/`` URI for the order that contains no private + nonce. Another wallet can use this URI with + :ts:op:`preparePayForUriV2` to claim and pay the order instead. The + payment can only be released before it is paid; use + :ts:op:`reclaimPayment` to claim the order again for this wallet. + + **Request:** + + The ``args`` must be an `UnclaimPaymentRequest` object. + + **Response:** + + On success, the result is an `UnclaimPaymentResult` object. + + **Side effects:** + + Unclaims the payment at the merchant and generates a taler://pay + URI that allows another wallet to complete it; changes the + transaction state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``, + ``WALLET_CORE_REQUEST_CANCELLED``, + ``WALLET_UNEXPECTED_REQUEST_ERROR``. + +.. ts:def:: UnclaimPaymentRequest + + interface UnclaimPaymentRequest { + // Transaction identifier of the payment to release. + transactionId: TransactionIdStr; + + // Enables progress correlation and cancellation through + // cancelProgressToken. + progressToken?: string; + } + +.. ts:def:: UnclaimPaymentResult + + interface UnclaimPaymentResult { + // Public taler://pay/ URI that another wallet can claim. + talerPayUri: TalerUriString; + } diff --git a/core/wallet-core/requests.rst b/core/wallet-core/requests.rst @@ -1,65 +0,0 @@ -.. _wallet-op-retryProgressTokenNow: - -**retryProgressTokenNow** - -Retry a long-running request now instead of waiting for its scheduled -retry delay. - -**Request:** - - The request ``args`` must be a `RetryProgressTokenNowRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - Long-running operations accept an optional ``progressToken`` in their - request. While such a request keeps failing, wallet-core retries it - with exponential backoff and reports each failed attempt via a - ``request-progress-error`` notification, including the delay until the - next retry. This operation abandons the remaining delay, so that the - next attempt starts immediately. If the progress token is unknown or - the request has no retry logic, the operation has no effect. - -.. ts:def:: RetryProgressTokenNowRequest - - interface RetryProgressTokenNowRequest { - // Name of the operation that the progress token belongs to. - operation: string; - - // Client-chosen progress token passed to the original request. - progressToken: string; - } - - -.. _wallet-op-cancelProgressToken: - -**cancelProgressToken** - -Cancel the running request associated with a progress token. - -**Request:** - - The request ``args`` must be a `CancelProgressTokenRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The affected request is aborted; its response is an error with error - code ``WALLET_CORE_REQUEST_CANCELLED``. If the progress token is - unknown, the operation has no effect. - -.. ts:def:: CancelProgressTokenRequest - - interface CancelProgressTokenRequest { - // Name of the operation that the progress token belongs to. - operation: string; - - // Client-chosen progress token passed to the original request. - progressToken: string; - } diff --git a/core/wallet-core/requests/cancel-progress-token.rst b/core/wallet-core/requests/cancel-progress-token.rst @@ -0,0 +1,30 @@ +.. ts:op:: cancelProgressToken + + Cancel the running request associated with a progress token. + + **Request:** + + The request ``args`` must be a `CancelProgressTokenRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Aborts the in-flight request; the request then fails with error code + ``WALLET_CORE_REQUEST_CANCELLED``. + + **Details:** + + If the progress token is unknown, the operation has no effect. + +.. ts:def:: CancelProgressTokenRequest + + interface CancelProgressTokenRequest { + // Name of the operation that the progress token belongs to. + operation: string; + + // Client-chosen progress token passed to the original request. + progressToken: string; + } diff --git a/core/wallet-core/requests/retry-progress-token-now.rst b/core/wallet-core/requests/retry-progress-token-now.rst @@ -0,0 +1,38 @@ +.. ts:op:: retryProgressTokenNow + + Retry a long-running request now instead of waiting for its scheduled + retry delay. + + **Request:** + + The request ``args`` must be a `RetryProgressTokenNowRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Abandons the remaining exponential-backoff delay of the pending + request; the request may restart immediately, causing network + activity. + + **Details:** + + Long-running operations accept an optional ``progressToken`` in their + request. While such a request keeps failing, wallet-core retries it + with exponential backoff and reports each failed attempt via a + ``request-progress-error`` notification, including the delay until the + next retry. This operation abandons the remaining delay, so that the + next attempt starts immediately. If the progress token is unknown or + the request has no retry logic, the operation has no effect. + +.. ts:def:: RetryProgressTokenNowRequest + + interface RetryProgressTokenNowRequest { + // Name of the operation that the progress token belongs to. + operation: string; + + // Client-chosen progress token passed to the original request. + progressToken: string; + } diff --git a/core/wallet-core/taldir.rst b/core/wallet-core/taldir.rst @@ -1,152 +0,0 @@ -.. _wallet-op-registerAlias: - -**registerAlias** - -Initiate the registration of an alias with a taldir (directory) -service. The alias (for example an e-mail address or a phone number) -is associated with a target URI, typically the wallet's mailbox URI. -Unless the registration is already paid for, the service sends a -challenge to the alias; the registration is completed with -:ref:`completeRegisterAlias <wallet-op-completeRegisterAlias>` once the -user has received the challenge. - -**Request:** - - The request arguments are a `TaldirRegistrationRequest` object. - -**Response:** - - On success, the result is a `TaldirRegistrationResponse`: an empty - object if a challenge was sent to the alias, or a - `TaldirAlreadyPaidResponse` object if the registration already - exists and is still paid for. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_ALIAS_REGISTRATION_FAILED``. - -.. ts:def:: TaldirRegistrationRequest - - interface TaldirRegistrationRequest { - // Alias to register, in alias-type-specific format. - alias: string; - - // Type of the alias, e.g. "email" or "sms". - aliasType: string; - - // Target URI to associate with the alias. - targetUri: string; - - // Base URL of the taldir service. - taldirBaseUrl: string; - - // For how long the registration should last or be extended. - duration: RelativeTime; - } - -.. ts:def:: TaldirRegistrationResponse - - type TaldirRegistrationResponse = - | TaldirAlreadyPaidResponse - | EmptyObject; - -.. ts:def:: TaldirAlreadyPaidResponse - - interface TaldirAlreadyPaidResponse { - // The remaining duration for which this registration is still - // paid for. - valid_for: RelativeTime; - } - - -.. _wallet-op-completeRegisterAlias: - -**completeRegisterAlias** - -Complete an alias registration started with -:ref:`registerAlias <wallet-op-registerAlias>` by answering the -challenge that the taldir service sent to the alias. - -**Request:** - - The request arguments are a `TaldirRegistrationCompletionRequest` - object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_ALIAS_REGISTRATION_FAILED``, ``GENERIC_FORBIDDEN``. - -**Details:** - - Wallet-core derives the answer sent to the service as the hash of the - ``challenge`` together with the ``targetUri`` from the registration - request, which protects the user against authorizing a concurrent - registration of a different target URI. ``GENERIC_FORBIDDEN`` - indicates that the service rejected the answer to the challenge. - -.. ts:def:: TaldirRegistrationCompletionRequest - - interface TaldirRegistrationCompletionRequest { - // Alias being registered. - alias: string; - - // Type of the alias. - aliasType: string; - - // Challenge received at the alias. - challenge: string; - - // Base URL of the taldir service. - taldirBaseUrl: string; - - // Target URI from the registration request. - targetUri: string; - } - - -.. _wallet-op-lookupAlias: - -**lookupAlias** - -Look up the target URI registered for an alias at a taldir service. - -**Request:** - - The request arguments are a `TaldirLookupRequest` object. - -**Response:** - - On success, the result is a `TaldirLookupResponse` object. The - ``targetUri`` field is absent if the alias is not registered, or if - the service does not support the requested alias type. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_ALIAS_REGISTRATION_FAILED``. - -.. ts:def:: TaldirLookupRequest - - interface TaldirLookupRequest { - // Alias to look up. - alias: string; - - // Type of the alias. - aliasType: string; - - // Base URL of the taldir service. - taldirBaseUrl: string; - } - -.. ts:def:: TaldirLookupResponse - - interface TaldirLookupResponse { - // Target URI registered for the alias, if any. - targetUri?: string; - } diff --git a/core/wallet-core/taldir/complete-register-alias.rst b/core/wallet-core/taldir/complete-register-alias.rst @@ -0,0 +1,52 @@ +.. ts:op:: completeRegisterAlias + + Complete an alias registration started with :ts:op:`registerAlias` + by answering the challenge that the taldir service sent to the + alias. + + **Request:** + + The request arguments are a `TaldirRegistrationCompletionRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Completes the pending alias registration at the taldir service by + submitting the challenge response over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_ALIAS_REGISTRATION_FAILED``, ``GENERIC_FORBIDDEN``. + + **Details:** + + Wallet-core derives the answer sent to the service as the hash of + the ``challenge`` together with the ``targetUri`` from the + registration request, which protects the user against authorizing + a concurrent registration of a different target URI. + ``GENERIC_FORBIDDEN`` indicates that the service rejected the + answer to the challenge. + +.. ts:def:: TaldirRegistrationCompletionRequest + + interface TaldirRegistrationCompletionRequest { + // Alias being registered. + alias: string; + + // Type of the alias. + aliasType: string; + + // Challenge received at the alias. + challenge: string; + + // Base URL of the taldir service. + taldirBaseUrl: string; + + // Target URI from the registration request. + targetUri: string; + } diff --git a/core/wallet-core/taldir/lookup-alias.rst b/core/wallet-core/taldir/lookup-alias.rst @@ -0,0 +1,43 @@ +.. ts:op:: lookupAlias + + Look up the target URI registered for an alias at a taldir service. + + **Request:** + + The request arguments are a `TaldirLookupRequest` object. + + **Response:** + + On success, the result is a `TaldirLookupResponse` object. The + ``targetUri`` field is absent if the alias is not registered, or + if the service does not support the requested alias type. + + **Side effects:** + + Queries the taldir directory service over the network; does not + change wallet state. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_ALIAS_REGISTRATION_FAILED``. + +.. ts:def:: TaldirLookupRequest + + interface TaldirLookupRequest { + // Alias to look up. + alias: string; + + // Type of the alias. + aliasType: string; + + // Base URL of the taldir service. + taldirBaseUrl: string; + } + +.. ts:def:: TaldirLookupResponse + + interface TaldirLookupResponse { + // Target URI registered for the alias, if any. + targetUri?: string; + } diff --git a/core/wallet-core/taldir/register-alias.rst b/core/wallet-core/taldir/register-alias.rst @@ -0,0 +1,63 @@ +.. ts:op:: registerAlias + + Initiate the registration of an alias with a taldir (directory) + service. The alias (for example an e-mail address or a phone + number) is associated with a target URI, typically the wallet's + mailbox URI. Unless the registration is already paid for, the + service sends a challenge to the alias; the registration is + completed with :ts:op:`completeRegisterAlias` once the user has + received the challenge. + + **Request:** + + The request arguments are a `TaldirRegistrationRequest` object. + + **Response:** + + On success, the result is a `TaldirRegistrationResponse`: an empty + object if a challenge was sent to the alias, or a + `TaldirAlreadyPaidResponse` object if the registration already + exists and is still paid for. + + **Side effects:** + + Requests the alias registration at the taldir service over the + network; the service may require payment for the registration. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_ALIAS_REGISTRATION_FAILED``. + +.. ts:def:: TaldirRegistrationRequest + + interface TaldirRegistrationRequest { + // Alias to register, in alias-type-specific format. + alias: string; + + // Type of the alias, e.g. "email" or "sms". + aliasType: string; + + // Target URI to associate with the alias. + targetUri: string; + + // Base URL of the taldir service. + taldirBaseUrl: string; + + // For how long the registration should last or be extended. + duration: RelativeTime; + } + +.. ts:def:: TaldirRegistrationResponse + + type TaldirRegistrationResponse = + | TaldirAlreadyPaidResponse + | EmptyObject; + +.. ts:def:: TaldirAlreadyPaidResponse + + interface TaldirAlreadyPaidResponse { + // The remaining duration for which this registration is still + // paid for. + valid_for: RelativeTime; + } diff --git a/core/wallet-core/testing.rst b/core/wallet-core/testing.rst @@ -1,1060 +0,0 @@ -.. _wallet-op-applyDevExperiment: - -**applyDevExperiment** - -Apply a developer experiment, specified as a ``taler://dev-experiment/`` -URI, to the current wallet state. This allows UI developers and testers -to play around without an elaborate test environment. Dev mode must be -active in the wallet. - -**Request:** - - The request must be an `ApplyDevExperimentRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: ApplyDevExperimentRequest - - interface ApplyDevExperimentRequest { - devExperimentUri: string; - } - - -.. _wallet-op-testingGetSampleTransactions: - -**testingGetSampleTransactions** - -Get sample transactions for UI development. Currently always returns -an empty transaction list. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `TransactionsResponse` object. - - -.. _wallet-op-withdrawTestkudos: - -**withdrawTestkudos** - -Make a withdrawal of ``TESTKUDOS:10`` from the test deployment at -test.taler.net (exchange ``https://exchange.test.taler.net/`` via the -corebank API at ``https://bank.test.taler.net/``). - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `WithdrawTestBalanceResult` object. - -.. ts:def:: WithdrawTestBalanceResult - - interface WithdrawTestBalanceResult { - // Transaction ID of the newly created withdrawal transaction. - transactionId: TransactionIdStr; - - // Account of the user registered for the withdrawal. - accountPaytoUri: string; - } - - -.. _wallet-op-withdrawTestBalance: - -**withdrawTestBalance** - -Make a withdrawal on a test deployment of the exchange and corebank. - -**Request:** - - The request must be a `WithdrawTestBalanceRequest` object. - -**Response:** - - On success, the result is a `WithdrawTestBalanceResult` object. - -**Details:** - - The operation registers a random bank user via the corebank API, - creates a withdrawal operation for the requested amount, accepts the - resulting withdrawal URI as a bank-integrated withdrawal at the given - exchange, and finally confirms the withdrawal operation at the bank. - -.. ts:def:: WithdrawTestBalanceRequest - - interface WithdrawTestBalanceRequest { - // Amount to withdraw. - amount: AmountString; - - // Corebank API base URL. - corebankApiBaseUrl: string; - - // Exchange to use for withdrawal. - exchangeBaseUrl: string; - - // Force the usage of a particular denomination selection. - // Only useful for testing. - forcedDenomSel?: ForcedDenomSel; - - // If set to true, treat the account created during - // the withdrawal as a foreign withdrawal account. - useForeignAccount?: boolean; - } - -.. ts:def:: ForcedDenomSel - - interface ForcedDenomSel { - denoms: { - value: AmountString; - count: number; - }[]; - } - - -.. _wallet-op-runIntegrationTest: - -**runIntegrationTest** - -Run a simple integration test on a test deployment of the exchange and -merchant: withdraw ``amountToWithdraw`` via the corebank API and then -spend ``amountToSpend`` at the merchant. - -**Request:** - - The request must be an `IntegrationTestArgs` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: IntegrationTestArgs - - interface IntegrationTestArgs { - exchangeBaseUrl: string; - corebankApiBaseUrl: string; - merchantBaseUrl: string; - merchantAuthToken?: string; - amountToWithdraw: AmountString; - amountToSpend: AmountString; - } - - -.. _wallet-op-runIntegrationTestV2: - -**runIntegrationTestV2** - -Run an integration test on a test deployment of the exchange and -merchant. The test withdraws a fixed amount in the exchange's currency -and then exercises payments, a refund, peer-to-peer push and pull -payments, and a deposit to a bank account. - -**Request:** - - The request must be an `IntegrationTestV2Args` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: IntegrationTestV2Args - - interface IntegrationTestV2Args { - exchangeBaseUrl: string; - corebankApiBaseUrl: string; - merchantBaseUrl: string; - merchantAuthToken?: string; - } - - -.. _wallet-op-dumpCoins: - -**dumpCoins** - -Dump all coins of the wallet in a simple JSON format. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `CoinDumpJson` object. - -.. ts:def:: CoinDumpJson - - interface CoinDumpJson { - coins: Array<{ - // The coin's denomination's public key. - denomPub: DenominationPubKey; - - // Hash of denom_pub. - denomPubHash: string; - - // Value of the denomination (without any fees). - denomValue: string; - - // Public key of the coin. - coinPub: string; - - // Base URL of the exchange for the coin. - exchangeBaseUrl: string; - - // Public key of the parent coin. - // Only present if this coin was obtained via refreshing. - refreshParentCoinPub: string | undefined; - - // Public key of the reserve for this coin. - // Only present if this coin was obtained via withdrawal. - withdrawalReservePub: string | undefined; - - // Status of the coin. - coinStatus: CoinStatus; - - // Information about the age restriction. - ageCommitmentProof: AgeCommitmentProof | undefined; - - history: WalletCoinHistoryItem[]; - }>; - } - -.. ts:def:: CoinStatus - - type CoinStatus = - // Withdrawn and never shown to anybody. - | "fresh" - // Coin was lost as the denomination is not usable anymore. - | "denom-loss" - // Fresh, but marked as suspended, thus won't be used for spending. - | "fresh-suspended" - // A coin that has been spent and refreshed. - | "dormant"; - -.. ts:def:: WalletCoinHistoryItem - - type WalletCoinHistoryItem = - | { - type: "withdraw"; - transactionId: TransactionIdStr; - } - | { - type: "spend"; - transactionId: TransactionIdStr; - amount: AmountString; - } - | { - type: "refresh"; - transactionId: TransactionIdStr; - amount: AmountString; - } - | { - type: "recoup"; - transactionId: TransactionIdStr; - amount: AmountString; - } - | { - type: "refund"; - transactionId: TransactionIdStr; - amount: AmountString; - }; - - -.. _wallet-op-testCrypto: - -**testCrypto** - -Test the crypto worker by hashing a fixed test string. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an unspecified JSON value. - - -.. _wallet-op-testPay: - -**testPay** - -Make a test payment using a test deployment of the exchange and -merchant. Creates an order for ``amount`` with the given ``summary`` -at the merchant and pays it. - -**Request:** - - The request must be a `TestPayArgs` object. - -**Response:** - - On success, the result is a `TestPayResult` object. - -.. ts:def:: TestPayArgs - - interface TestPayArgs { - merchantBaseUrl: string; - merchantAuthToken?: string; - amount: AmountString; - summary: string; - forcedCoinSel?: ForcedCoinSel; - } - -.. ts:def:: ForcedCoinSel - - // Forced coin selection for deposits/payments. - interface ForcedCoinSel { - coins: { - value: AmountString; - contribution: AmountString; - }[]; - } - -.. ts:def:: TestPayResult - - interface TestPayResult { - // Number of coins used for the payment. - numCoins: number; - } - - -.. _wallet-op-setCoinSuspended: - -**setCoinSuspended** - -Set a coin as (un-)suspended. Suspended coins won't be used for -payments. - -**Request:** - - The request must be a `SetCoinSuspendedRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: SetCoinSuspendedRequest - - interface SetCoinSuspendedRequest { - coinPub: string; - suspended: boolean; - } - - -.. _wallet-op-forceRefresh: - -**forceRefresh** - -Force a refresh on coins where it would not be necessary. Creates a -manual refresh group for the given coins. - -**Request:** - - The request must be a `ForceRefreshRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The request fails if ``refreshCoinSpecs`` is empty or names a coin - that is unknown to the wallet. Each coin is refreshed for its full - denomination value, unless a smaller ``amount`` is given. - -.. ts:def:: ForceRefreshRequest - - interface ForceRefreshRequest { - refreshCoinSpecs: RefreshCoinSpec[]; - } - -.. ts:def:: RefreshCoinSpec - - interface RefreshCoinSpec { - coinPub: string; - - // Amount to refresh; defaults to the coin's denomination value. - amount?: AmountString; - } - - -.. _wallet-op-testingWaitTransactionsFinal: - -**testingWaitTransactionsFinal** - -Wait until all transactions are in a final state. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - - -.. _wallet-op-testingWaitRefreshesFinal: - -**testingWaitRefreshesFinal** - -Wait until all refresh transactions are in a final state. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - - -.. _wallet-op-testingWaitTransactionState: - -**testingWaitTransactionState** - -This operation is a legacy alias of -:ref:`waitTransactionState <wallet-op-waitTransactionState>`. It waits -until a transaction is in a particular state and has the same behavior -and payloads; the legacy type names `TestingWaitTransactionRequest` and -`TestingWaitTransactionStateResponse` are aliases of -`WaitTransactionStateRequest` and `WaitTransactionStateResponse`, -respectively. - -**Request:** - - The request must be a `WaitTransactionStateRequest` object. - -**Response:** - - On success, the result is a `WaitTransactionStateResponse` object. - - -.. _wallet-op-testingWaitExchangeState: - -**testingWaitExchangeState** - -Wait until an exchange entry is in a particular state. Currently, only -the wallet KYC status of the exchange entry can be waited on. - -**Request:** - - The request must be a `TestingWaitExchangeStateRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: TestingWaitExchangeStateRequest - - interface TestingWaitExchangeStateRequest { - exchangeBaseUrl: string; - walletKycStatus?: ExchangeWalletKycStatus; - } - -.. ts:def:: ExchangeWalletKycStatus - - type ExchangeWalletKycStatus = - | "done" - // Wallet needs to request KYC status. - | "legi-init" - // User requires KYC or AML. - | "legi"; - - -.. _wallet-op-testingWaitExchangeReady: - -**testingWaitExchangeReady** - -Wait until an exchange entry is ready. Returns an error if updating -the exchange failed. - -**Request:** - - The request must be a `TestingWaitExchangeReadyRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: TestingWaitExchangeReadyRequest - - interface TestingWaitExchangeReadyRequest { - exchangeBaseUrl: string; - - // Do not stop waiting even when the exchange is - // in an error state. - noBail?: boolean; - - // Force waiting until an update really happened. - forceUpdate?: boolean; - - // Only consider the exchange as ready if the - // next auto-refresh is scheduled for the future. - waitAutoRefresh?: boolean; - } - - -.. _wallet-op-testingWaitTasksDone: - -**testingWaitTasksDone** - -Wait until all pending tasks of the wallet are done. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - - -.. _wallet-op-testingWaitBalance: - -**testingWaitBalance** - -Wait until a balance has reached the desired value. Waits until the -material balance in the currency of ``amount`` equals ``amount``; only -``type`` ``"material"`` is currently supported. - -**Request:** - - The request must be a `TestingWaitBalanceRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: TestingWaitBalanceRequest - - interface TestingWaitBalanceRequest { - type: "material" | "available"; - amount: AmountString; - } - - -.. _wallet-op-testingGetDbStats: - -**testingGetDbStats** - -Get database statistics. The returned statistics are specific to the -database backend in use. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an unspecified JSON value. - - -.. _wallet-op-testingSetTimetravel: - -**testingSetTimetravel** - -Add an offset to the wallet's internal time. - -**Request:** - - The request must be a `TestingSetTimetravelRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: TestingSetTimetravelRequest - - interface TestingSetTimetravelRequest { - // Offset added to the wallet's internal time, in milliseconds. - offsetMs: number; - } - - -.. _wallet-op-testingGetDenomStats: - -**testingGetDenomStats** - -Get statistics about the denominations of an exchange known to the -wallet. - -**Request:** - - The request must be a `TestingGetDenomStatsRequest` object. - -**Response:** - - On success, the result is a `TestingGetDenomStatsResponse` object. - -.. ts:def:: TestingGetDenomStatsRequest - - interface TestingGetDenomStatsRequest { - exchangeBaseUrl: string; - } - -.. ts:def:: TestingGetDenomStatsResponse - - interface TestingGetDenomStatsResponse { - // Denominations known to the wallet for the exchange. - numKnown: number; - - // Denominations currently offered by the exchange. - numOffered: number; - - // Denominations that are not usable anymore. - numLost: number; - } - - -.. _wallet-op-testingRecoverCoins: - -**testingRecoverCoins** - -Follow the refresh/link recovery chain for coins at one exchange. - -**Request:** - - The request must be a `TestingRecoverCoinsRequest` object. - -**Response:** - - On success, the result is a `TestingRecoverCoinsResponse` object. - -.. ts:def:: TestingRecoverCoinsRequest - - interface TestingRecoverCoinsRequest { - exchangeBaseUrl: string; - - // Start with fresh coins by default. - // Unfinished recovery always resumes. - onlyFresh?: boolean; - - progressToken?: string; - } - -.. ts:def:: TestingRecoverCoinsResponse - - interface TestingRecoverCoinsResponse { - exchangeBaseUrl: string; - progressToken: string; - - // False when a coin or residual refresh remains unfinished. - complete: boolean; - - // Histories processed during this invocation. - numChecked: number; - - numDiscovered: number; - - numQueued: number; - - // Newly imported coins that are now spendable, - // including residual refreshes. - numRecovered: number; - - // Cumulative for a resumed recovery; excludes existing coins, - // spent ancestors, fees and pending refreshes. - recoveredAmount: AmountString; - - issues: CoinRecoveryIssue[]; - } - -.. ts:def:: CoinRecoveryIssue - - interface CoinRecoveryIssue { - coinPub: string; - refreshCommitment?: string; - reason: - | "missing-recovery-data" - | "missing-denomination" - | "invalid-history" - | "commitment-mismatch" - | "request-failed" - | "local-data-changed" - | "refresh-incomplete" - | "coin-unavailable"; - description: string; - } - - -.. _wallet-op-testingCheckCoins: - -**testingCheckCoins** - -Validate exchange coin histories and compare balances of unspent coins -against the exchange's view. - -**Request:** - - The request must be a `TestingCheckCoinsRequest` object. - -**Response:** - - On success, the result is a `TestingCheckCoinsResponse` object. - -.. ts:def:: TestingCheckCoinsRequest - - interface TestingCheckCoinsRequest { - // Canonicalized before selecting coins. - // Only this exchange is contacted. - exchangeBaseUrl: string; - - // Default true: check only fresh coins. False validates every coin - // status, comparing denomination value only for fresh or - // suspended-fresh coins. - onlyFresh?: boolean; - } - -.. ts:def:: TestingCheckCoinsResponse - - interface TestingCheckCoinsResponse { - exchangeBaseUrl: string; - - // Denomination value of fresh coins under the exchange's current - // master key in the initial snapshot. Null if local data is - // unavailable. - expectedMaterialBalance: AmountString | null; - - // Verified remaining exchange balance of those same coins. Null - // if any relevant history could not be verified or the material - // balance changed during the check; never a partial total. - actualMaterialBalance: AmountString | null; - - // Coins selected by the exchange and onlyFresh filter - // in the initial snapshot. - numCoins: number; - - // Validated histories with stable coin data, - // including balance mismatches. - numChecked: number; - - // Balance differences, invalid exchange histories, - // and unavailable coin data. - issues: TestingCheckCoinsIssue[]; - } - -.. ts:def:: TestingCheckCoinsIssue - - interface TestingCheckCoinsIssue { - coinPub: string; - denomPubHash: string; - category: "mismatch" | "error" | "incomplete"; - reason: - | "balance-difference" - | "invalid-history" - | "request-failed" - | "missing-local-data" - | "local-data-changed"; - description: string; - expected?: Record<string, string | number | boolean>; - actual?: Record<string, string | number | boolean>; - - // On balance differences: exchange history in offset order, - // including credits. - exchangeOperations?: Array< - [operation: CoinSpendHistoryItem["type"], amount: AmountString] - >; - } - - -.. _wallet-op-testingPing: - -**testingPing** - -Do nothing. Can be used to check that wallet-core is alive and -responding to requests. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - - -.. _wallet-op-testingGetReserveHistory: - -**testingGetReserveHistory** - -Fetch the history of a reserve from the exchange. The reserve must be -known to the wallet, as the request to the exchange is signed with the -reserve's private key. - -**Request:** - - The request must be a `TestingGetReserveHistoryRequest` object. - -**Response:** - - On success, the result is an unspecified JSON value with the reserve - history as returned by the exchange. - -.. ts:def:: TestingGetReserveHistoryRequest - - interface TestingGetReserveHistoryRequest { - reservePub: string; - exchangeBaseUrl: string; - } - - -.. _wallet-op-testingResetAllRetries: - -**testingResetAllRetries** - -Reset all task/transaction retries, resulting in an immediate re-try of -all pending operations. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is an empty object. - - -.. _wallet-op-testingWaitWalletKyc: - -**testingWaitWalletKyc** - -Wait for the wallet KYC process at an exchange. With ``passed`` set to -true, waits until KYC has been passed for at least the given ``amount``; -otherwise already returns when legitimization is required. - -**Request:** - - The request must be a `TestingWaitWalletKycRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: TestingWaitWalletKycRequest - - interface TestingWaitWalletKycRequest { - exchangeBaseUrl: string; - amount: AmountString; - - // Do we wait for the KYC to be passed (true), - // or do we already return if legitimization is - // required (false). - passed: boolean; - } - - -.. _wallet-op-testingPlanMigrateExchangeBaseUrl: - -**testingPlanMigrateExchangeBaseUrl** - -Enable migration from an old exchange base URL to a new exchange base -URL. The actual migration is only applied once the exchange returns -the new base URL. - -**Request:** - - The request must be a `TestingPlanMigrateExchangeBaseUrlRequest` - object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: TestingPlanMigrateExchangeBaseUrlRequest - - interface TestingPlanMigrateExchangeBaseUrlRequest { - oldExchangeBaseUrl: string; - newExchangeBaseUrl: string; - } - - -.. _wallet-op-testingRunFixup: - -**testingRunFixup** - -Run a named database fixup that repairs records in the wallet database. -Fails if no fixup with the given ``id`` exists. - -**Request:** - - The request must be a `RunFixupRequest` object. - -**Response:** - - On success, the result is an empty object. - -.. ts:def:: RunFixupRequest - - interface RunFixupRequest { - // Name of the fixup to run. - id: string; - } - - -.. _wallet-op-testingGetFlightRecords: - -**testingGetFlightRecords** - -Get the wallet's flight records: records of exceptional events observed -while communicating with an exchange (such as a coin reported as gone -during a refresh melt, or a withdrawal requiring redenomination), kept -for post-mortem debugging. - -**Request:** - - This operation takes no arguments (an empty object). - -**Response:** - - On success, the result is a `TestingGetFlightRecordsResponse` object. - -.. ts:def:: TestingGetFlightRecordsResponse - - interface TestingGetFlightRecordsResponse { - flightRecords: FlightRecordEntry[]; - } - -.. ts:def:: FlightRecordEntry - - interface FlightRecordEntry { - timestamp: TalerPreciseTimestamp; - target: string; - event: FlightRecordEvent; - } - -.. ts:def:: FlightRecordEvent - - type FlightRecordEvent = "melt-gone" | "withdrawal-redenominate"; - - -.. _wallet-op-testingGetPerformanceStats: - -**testingGetPerformanceStats** - -Get a list of performance stats for diagnostics. Requires -observability events to be enabled; performance tables for the current -running wallet instance are generated from observability events and -stored in memory. Under each table, different types of duration for -each operation (e.g. a ``getBalances`` wallet request) are included. - -**Request:** - - The request must be a `GetPerformanceStatsRequest` object. - -**Response:** - - On success, the result is a `GetPerformanceStatsResponse` object. - -.. ts:def:: GetPerformanceStatsRequest - - interface GetPerformanceStatsRequest { - // Limit to N largest average performance stats of each table. - // When undefined, all performance stats will be returned. - limit?: number; - } - -.. ts:def:: GetPerformanceStatsResponse - - interface GetPerformanceStatsResponse { - stats: PerformanceTable; - } - -.. ts:def:: PerformanceTable - - type PerformanceTable = { - [key in PerformanceStatType]?: PerformanceStat[]; - }; - -.. ts:def:: PerformanceStatType - - type PerformanceStatType = - | "http-fetch" - | "db-query" - | "crypto" - | "wallet-request" - | "wallet-task"; - -.. ts:def:: PerformanceStat - - type PerformanceStat = - | { - type: "http-fetch"; - url: string; - avgDurationMs: number; - maxDurationMs: number; - minDurationMs: number; - totalDurationMs: number; - count: number; - } - | { - type: "db-query"; - name: string; - location: string; - avgDurationMs: number; - maxDurationMs: number; - minDurationMs: number; - totalDurationMs: number; - count: number; - } - | { - type: "crypto"; - operation: string; - avgDurationMs: number; - maxDurationMs: number; - minDurationMs: number; - totalDurationMs: number; - count: number; - } - | { - type: "wallet-request"; - operation: string; - avgDurationMs: number; - maxDurationMs: number; - minDurationMs: number; - totalDurationMs: number; - count: number; - } - | { - type: "wallet-task"; - taskId: string; - avgDurationMs: number; - maxDurationMs: number; - minDurationMs: number; - totalDurationMs: number; - count: number; - }; - - -.. _wallet-op-testingCorruptWithdrawalCoinSel: - -**testingCorruptWithdrawalCoinSel** - -Corrupt the denomination selection of a withdrawal transaction by -replacing the first selected denomination's public key hash with a -random value, in order to test the wallet's error handling. - -**Request:** - - The request must be a `TestingCorruptWithdrawalCoinSelRequest` - object. - -**Response:** - - On success, the result is an empty object. - -**Details:** - - The ``transactionId`` must identify a withdrawal transaction. - Withdrawal groups without a denomination selection are left - unchanged. - -.. ts:def:: TestingCorruptWithdrawalCoinSelRequest - - interface TestingCorruptWithdrawalCoinSelRequest { - transactionId: TransactionIdStr; - } diff --git a/core/wallet-core/testing/apply-dev-experiment.rst b/core/wallet-core/testing/apply-dev-experiment.rst @@ -0,0 +1,25 @@ +.. ts:op:: applyDevExperiment + + Apply a developer experiment, specified as a ``taler://dev-experiment/`` + URI, to the current wallet state. This allows UI developers and + testers to play around without an elaborate test environment. Dev + mode must be active in the wallet. + + **Request:** + + The request must be an `ApplyDevExperimentRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Applies arbitrary changes to the wallet's state and database, + depending on the experiment. + +.. ts:def:: ApplyDevExperimentRequest + + interface ApplyDevExperimentRequest { + devExperimentUri: string; + } diff --git a/core/wallet-core/testing/dump-coins.rst b/core/wallet-core/testing/dump-coins.rst @@ -0,0 +1,89 @@ +.. ts:op:: dumpCoins + :read-only: + + Dump all coins of the wallet in a simple JSON format. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `CoinDumpJson` object. + +.. ts:def:: CoinDumpJson + + interface CoinDumpJson { + coins: Array<{ + // The coin's denomination's public key. + denomPub: DenominationPubKey; + + // Hash of denom_pub. + denomPubHash: string; + + // Value of the denomination (without any fees). + denomValue: string; + + // Public key of the coin. + coinPub: string; + + // Base URL of the exchange for the coin. + exchangeBaseUrl: string; + + // Public key of the parent coin. + // Only present if this coin was obtained via refreshing. + refreshParentCoinPub: string | undefined; + + // Public key of the reserve for this coin. + // Only present if this coin was obtained via withdrawal. + withdrawalReservePub: string | undefined; + + // Status of the coin. + coinStatus: CoinStatus; + + // Information about the age restriction. + ageCommitmentProof: AgeCommitmentProof | undefined; + + history: WalletCoinHistoryItem[]; + }>; + } + +.. ts:def:: CoinStatus + + type CoinStatus = + // Withdrawn and never shown to anybody. + | "fresh" + // Coin was lost as the denomination is not usable anymore. + | "denom-loss" + // Fresh, but marked as suspended, thus won't be used for spending. + | "fresh-suspended" + // A coin that has been spent and refreshed. + | "dormant"; + +.. ts:def:: WalletCoinHistoryItem + + type WalletCoinHistoryItem = + | { + type: "withdraw"; + transactionId: TransactionIdStr; + } + | { + type: "spend"; + transactionId: TransactionIdStr; + amount: AmountString; + } + | { + type: "refresh"; + transactionId: TransactionIdStr; + amount: AmountString; + } + | { + type: "recoup"; + transactionId: TransactionIdStr; + amount: AmountString; + } + | { + type: "refund"; + transactionId: TransactionIdStr; + amount: AmountString; + }; diff --git a/core/wallet-core/testing/force-refresh.rst b/core/wallet-core/testing/force-refresh.rst @@ -0,0 +1,39 @@ +.. ts:op:: forceRefresh + + Force a refresh on coins where it would not be necessary. Creates a + manual refresh group for the given coins. + + **Request:** + + The request must be a `ForceRefreshRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Refreshes the given coins at the exchange over the network even + where a refresh would not be necessary; creates refresh + transactions. + + **Details:** + + The request fails if ``refreshCoinSpecs`` is empty or names a coin + that is unknown to the wallet. Each coin is refreshed for its full + denomination value, unless a smaller ``amount`` is given. + +.. ts:def:: ForceRefreshRequest + + interface ForceRefreshRequest { + refreshCoinSpecs: RefreshCoinSpec[]; + } + +.. ts:def:: RefreshCoinSpec + + interface RefreshCoinSpec { + coinPub: string; + + // Amount to refresh; defaults to the coin's denomination value. + amount?: AmountString; + } diff --git a/core/wallet-core/testing/run-integration-test-v2.rst b/core/wallet-core/testing/run-integration-test-v2.rst @@ -0,0 +1,28 @@ +.. ts:op:: runIntegrationTestV2 + + Run an integration test on a test deployment of the exchange and + merchant. The test withdraws a fixed amount in the exchange's + currency and then exercises payments, a refund, peer-to-peer push and + pull payments, and a deposit to a bank account. + + **Request:** + + The request must be an `IntegrationTestV2Args` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Performs real withdrawals, payments, a refund, peer-to-peer + payments and a deposit at the test deployment over the network. + +.. ts:def:: IntegrationTestV2Args + + interface IntegrationTestV2Args { + exchangeBaseUrl: string; + corebankApiBaseUrl: string; + merchantBaseUrl: string; + merchantAuthToken?: string; + } diff --git a/core/wallet-core/testing/run-integration-test.rst b/core/wallet-core/testing/run-integration-test.rst @@ -0,0 +1,29 @@ +.. ts:op:: runIntegrationTest + + Run a simple integration test on a test deployment of the exchange + and merchant: withdraw ``amountToWithdraw`` via the corebank API and + then spend ``amountToSpend`` at the merchant. + + **Request:** + + The request must be an `IntegrationTestArgs` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Performs a real withdrawal and a real payment at the test + deployment over the network. + +.. ts:def:: IntegrationTestArgs + + interface IntegrationTestArgs { + exchangeBaseUrl: string; + corebankApiBaseUrl: string; + merchantBaseUrl: string; + merchantAuthToken?: string; + amountToWithdraw: AmountString; + amountToSpend: AmountString; + } diff --git a/core/wallet-core/testing/set-coin-suspended.rst b/core/wallet-core/testing/set-coin-suspended.rst @@ -0,0 +1,24 @@ +.. ts:op:: setCoinSuspended + + Set a coin as (un-)suspended. Suspended coins won't be used for + payments. + + **Request:** + + The request must be a `SetCoinSuspendedRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Marks the coin as (un-)suspended; suspended coins are excluded from + payments. + +.. ts:def:: SetCoinSuspendedRequest + + interface SetCoinSuspendedRequest { + coinPub: string; + suspended: boolean; + } diff --git a/core/wallet-core/testing/test-crypto.rst b/core/wallet-core/testing/test-crypto.rst @@ -0,0 +1,12 @@ +.. ts:op:: testCrypto + :read-only: + + Test the crypto worker by hashing a fixed test string. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an unspecified JSON value. diff --git a/core/wallet-core/testing/test-pay.rst b/core/wallet-core/testing/test-pay.rst @@ -0,0 +1,45 @@ +.. ts:op:: testPay + + Make a test payment using a test deployment of the exchange and + merchant. Creates an order for ``amount`` with the given ``summary`` + at the merchant and pays it. + + **Request:** + + The request must be a `TestPayArgs` object. + + **Response:** + + On success, the result is a `TestPayResult` object. + + **Side effects:** + + Creates an order and makes a real payment at the test merchant over + the network. + +.. ts:def:: TestPayArgs + + interface TestPayArgs { + merchantBaseUrl: string; + merchantAuthToken?: string; + amount: AmountString; + summary: string; + forcedCoinSel?: ForcedCoinSel; + } + +.. ts:def:: ForcedCoinSel + + // Forced coin selection for deposits/payments. + interface ForcedCoinSel { + coins: { + value: AmountString; + contribution: AmountString; + }[]; + } + +.. ts:def:: TestPayResult + + interface TestPayResult { + // Number of coins used for the payment. + numCoins: number; + } diff --git a/core/wallet-core/testing/testing-check-coins.rst b/core/wallet-core/testing/testing-check-coins.rst @@ -0,0 +1,81 @@ +.. ts:op:: testingCheckCoins + + Validate exchange coin histories and compare balances of unspent + coins against the exchange's view. + + **Request:** + + The request must be a `TestingCheckCoinsRequest` object. + + **Response:** + + On success, the result is a `TestingCheckCoinsResponse` object. + + **Side effects:** + + Queries the coin history from the exchange over the network for + every checked coin. + +.. ts:def:: TestingCheckCoinsRequest + + interface TestingCheckCoinsRequest { + // Canonicalized before selecting coins. + // Only this exchange is contacted. + exchangeBaseUrl: string; + + // Default true: check only fresh coins. False validates every coin + // status, comparing denomination value only for fresh or + // suspended-fresh coins. + onlyFresh?: boolean; + } + +.. ts:def:: TestingCheckCoinsResponse + + interface TestingCheckCoinsResponse { + exchangeBaseUrl: string; + + // Denomination value of fresh coins under the exchange's current + // master key in the initial snapshot. Null if local data is + // unavailable. + expectedMaterialBalance: AmountString | null; + + // Verified remaining exchange balance of those same coins. Null + // if any relevant history could not be verified or the material + // balance changed during the check; never a partial total. + actualMaterialBalance: AmountString | null; + + // Coins selected by the exchange and onlyFresh filter + // in the initial snapshot. + numCoins: number; + + // Validated histories with stable coin data, + // including balance mismatches. + numChecked: number; + + // Balance differences, invalid exchange histories, + // and unavailable coin data. + issues: TestingCheckCoinsIssue[]; + } + +.. ts:def:: TestingCheckCoinsIssue + + interface TestingCheckCoinsIssue { + coinPub: string; + denomPubHash: string; + category: "mismatch" | "error" | "incomplete"; + reason: + | "balance-difference" + | "invalid-history" + | "request-failed" + | "missing-local-data" + | "local-data-changed"; + description: string; + expected?: Record<string, string | number | boolean>; + actual?: Record<string, string | number | boolean>; + + // On balance differences: exchange history in offset order, + // including credits. + exchangeOperations?: Array< + [operation: CoinSpendHistoryItem["type"], amount: AmountString] + >; + } diff --git a/core/wallet-core/testing/testing-corrupt-withdrawal-coin-sel.rst b/core/wallet-core/testing/testing-corrupt-withdrawal-coin-sel.rst @@ -0,0 +1,31 @@ +.. ts:op:: testingCorruptWithdrawalCoinSel + + Corrupt the denomination selection of a withdrawal transaction by + replacing the first selected denomination's public key hash with a + random value, in order to test the wallet's error handling. + + **Request:** + + The request must be a `TestingCorruptWithdrawalCoinSelRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Intentionally corrupts the withdrawal's coin selection in the + wallet database (test failure injection). + + **Details:** + + The ``transactionId`` must identify a withdrawal transaction. + Withdrawal groups without a denomination selection are left + unchanged. + +.. ts:def:: TestingCorruptWithdrawalCoinSelRequest + + interface TestingCorruptWithdrawalCoinSelRequest { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/testing/testing-get-db-stats.rst b/core/wallet-core/testing/testing-get-db-stats.rst @@ -0,0 +1,13 @@ +.. ts:op:: testingGetDbStats + :read-only: + + Get database statistics. The returned statistics are specific to the + database backend in use. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an unspecified JSON value. diff --git a/core/wallet-core/testing/testing-get-denom-stats.rst b/core/wallet-core/testing/testing-get-denom-stats.rst @@ -0,0 +1,32 @@ +.. ts:op:: testingGetDenomStats + :read-only: + + Get statistics about the denominations of an exchange known to the + wallet. + + **Request:** + + The request must be a `TestingGetDenomStatsRequest` object. + + **Response:** + + On success, the result is a `TestingGetDenomStatsResponse` object. + +.. ts:def:: TestingGetDenomStatsRequest + + interface TestingGetDenomStatsRequest { + exchangeBaseUrl: string; + } + +.. ts:def:: TestingGetDenomStatsResponse + + interface TestingGetDenomStatsResponse { + // Denominations known to the wallet for the exchange. + numKnown: number; + + // Denominations currently offered by the exchange. + numOffered: number; + + // Denominations that are not usable anymore. + numLost: number; + } diff --git a/core/wallet-core/testing/testing-get-flight-records.rst b/core/wallet-core/testing/testing-get-flight-records.rst @@ -0,0 +1,34 @@ +.. ts:op:: testingGetFlightRecords + :read-only: + + Get the wallet's flight records: records of exceptional events + observed while communicating with an exchange (such as a coin + reported as gone during a refresh melt, or a withdrawal requiring + redenomination), kept for post-mortem debugging. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `TestingGetFlightRecordsResponse` + object. + +.. ts:def:: TestingGetFlightRecordsResponse + + interface TestingGetFlightRecordsResponse { + flightRecords: FlightRecordEntry[]; + } + +.. ts:def:: FlightRecordEntry + + interface FlightRecordEntry { + timestamp: TalerPreciseTimestamp; + target: string; + event: FlightRecordEvent; + } + +.. ts:def:: FlightRecordEvent + + type FlightRecordEvent = "melt-gone" | "withdrawal-redenominate"; diff --git a/core/wallet-core/testing/testing-get-performance-stats.rst b/core/wallet-core/testing/testing-get-performance-stats.rst @@ -0,0 +1,96 @@ +.. ts:op:: testingGetPerformanceStats + :read-only: + + Get a list of performance stats for diagnostics. Requires + observability events to be enabled; performance tables for the + current running wallet instance are generated from observability + events and stored in memory. Under each table, different types of + duration for each operation (e.g. a ``getBalances`` wallet request) + are included. + + **Request:** + + The request must be a `GetPerformanceStatsRequest` object. + + **Response:** + + On success, the result is a `GetPerformanceStatsResponse` object. + +.. ts:def:: GetPerformanceStatsRequest + + interface GetPerformanceStatsRequest { + // Limit to N largest average performance stats of each table. + // When undefined, all performance stats will be returned. + limit?: number; + } + +.. ts:def:: GetPerformanceStatsResponse + + interface GetPerformanceStatsResponse { + stats: PerformanceTable; + } + +.. ts:def:: PerformanceTable + + type PerformanceTable = { + [key in PerformanceStatType]?: PerformanceStat[]; + }; + +.. ts:def:: PerformanceStatType + + type PerformanceStatType = + | "http-fetch" + | "db-query" + | "crypto" + | "wallet-request" + | "wallet-task"; + +.. ts:def:: PerformanceStat + + type PerformanceStat = + | { + type: "http-fetch"; + url: string; + avgDurationMs: number; + maxDurationMs: number; + minDurationMs: number; + totalDurationMs: number; + count: number; + } + | { + type: "db-query"; + name: string; + location: string; + avgDurationMs: number; + maxDurationMs: number; + minDurationMs: number; + totalDurationMs: number; + count: number; + } + | { + type: "crypto"; + operation: string; + avgDurationMs: number; + maxDurationMs: number; + minDurationMs: number; + totalDurationMs: number; + count: number; + } + | { + type: "wallet-request"; + operation: string; + avgDurationMs: number; + maxDurationMs: number; + minDurationMs: number; + totalDurationMs: number; + count: number; + } + | { + type: "wallet-task"; + taskId: string; + avgDurationMs: number; + maxDurationMs: number; + minDurationMs: number; + totalDurationMs: number; + count: number; + }; diff --git a/core/wallet-core/testing/testing-get-reserve-history.rst b/core/wallet-core/testing/testing-get-reserve-history.rst @@ -0,0 +1,25 @@ +.. ts:op:: testingGetReserveHistory + + Fetch the history of a reserve from the exchange. The reserve must + be known to the wallet, as the request to the exchange is signed with + the reserve's private key. + + **Request:** + + The request must be a `TestingGetReserveHistoryRequest` object. + + **Response:** + + On success, the result is an unspecified JSON value with the + reserve history as returned by the exchange. + + **Side effects:** + + Queries the reserve history from the exchange over the network. + +.. ts:def:: TestingGetReserveHistoryRequest + + interface TestingGetReserveHistoryRequest { + reservePub: string; + exchangeBaseUrl: string; + } diff --git a/core/wallet-core/testing/testing-get-sample-transactions.rst b/core/wallet-core/testing/testing-get-sample-transactions.rst @@ -0,0 +1,13 @@ +.. ts:op:: testingGetSampleTransactions + :read-only: + + Get sample transactions for UI development. Currently always returns + an empty transaction list. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `TransactionsResponse` object. diff --git a/core/wallet-core/testing/testing-ping.rst b/core/wallet-core/testing/testing-ping.rst @@ -0,0 +1,13 @@ +.. ts:op:: testingPing + :read-only: + + Do nothing. Can be used to check that wallet-core is alive and + responding to requests. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. diff --git a/core/wallet-core/testing/testing-plan-migrate-exchange-base-url.rst b/core/wallet-core/testing/testing-plan-migrate-exchange-base-url.rst @@ -0,0 +1,26 @@ +.. ts:op:: testingPlanMigrateExchangeBaseUrl + + Enable migration from an old exchange base URL to a new exchange base + URL. The actual migration is only applied once the exchange returns + the new base URL. + + **Request:** + + The request must be a `TestingPlanMigrateExchangeBaseUrlRequest` + object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Enables a pending exchange base URL migration, which is applied + once the exchange advertises the new base URL. + +.. ts:def:: TestingPlanMigrateExchangeBaseUrlRequest + + interface TestingPlanMigrateExchangeBaseUrlRequest { + oldExchangeBaseUrl: string; + newExchangeBaseUrl: string; + } diff --git a/core/wallet-core/testing/testing-recover-coins.rst b/core/wallet-core/testing/testing-recover-coins.rst @@ -0,0 +1,73 @@ +.. ts:op:: testingRecoverCoins + + Follow the refresh/link recovery chain for coins at one exchange. + + **Request:** + + The request must be a `TestingRecoverCoinsRequest` object. + + **Response:** + + On success, the result is a `TestingRecoverCoinsResponse` object. + + **Side effects:** + + Scans the exchange for coins belonging to the wallet over the + network and re-claims the coins it finds, creating transactions and + emitting notifications. + +.. ts:def:: TestingRecoverCoinsRequest + + interface TestingRecoverCoinsRequest { + exchangeBaseUrl: string; + + // Start with fresh coins by default. + // Unfinished recovery always resumes. + onlyFresh?: boolean; + + progressToken?: string; + } + +.. ts:def:: TestingRecoverCoinsResponse + + interface TestingRecoverCoinsResponse { + exchangeBaseUrl: string; + progressToken: string; + + // False when a coin or residual refresh remains unfinished. + complete: boolean; + + // Histories processed during this invocation. + numChecked: number; + + numDiscovered: number; + + numQueued: number; + + // Newly imported coins that are now spendable, + // including residual refreshes. + numRecovered: number; + + // Cumulative for a resumed recovery; excludes existing coins, + // spent ancestors, fees and pending refreshes. + recoveredAmount: AmountString; + + issues: CoinRecoveryIssue[]; + } + +.. ts:def:: CoinRecoveryIssue + + interface CoinRecoveryIssue { + coinPub: string; + refreshCommitment?: string; + reason: + | "missing-recovery-data" + | "missing-denomination" + | "invalid-history" + | "commitment-mismatch" + | "request-failed" + | "local-data-changed" + | "refresh-incomplete" + | "coin-unavailable"; + description: string; + } diff --git a/core/wallet-core/testing/testing-reset-all-retries.rst b/core/wallet-core/testing/testing-reset-all-retries.rst @@ -0,0 +1,17 @@ +.. ts:op:: testingResetAllRetries + + Reset all task/transaction retries, resulting in an immediate re-try + of all pending operations. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Resets the retry backoff of all tasks and transactions, causing + immediate retries and thus network activity. diff --git a/core/wallet-core/testing/testing-run-fixup.rst b/core/wallet-core/testing/testing-run-fixup.rst @@ -0,0 +1,23 @@ +.. ts:op:: testingRunFixup + + Run a named database fixup that repairs records in the wallet + database. Fails if no fixup with the given ``id`` exists. + + **Request:** + + The request must be a `RunFixupRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Runs a database fixup that modifies stored data. + +.. ts:def:: RunFixupRequest + + interface RunFixupRequest { + // Name of the fixup to run. + id: string; + } diff --git a/core/wallet-core/testing/testing-set-timetravel.rst b/core/wallet-core/testing/testing-set-timetravel.rst @@ -0,0 +1,23 @@ +.. ts:op:: testingSetTimetravel + + Add an offset to the wallet's internal time. + + **Request:** + + The request must be a `TestingSetTimetravelRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Offsets the wallet's internal clock, affecting all time-based logic + of the wallet. + +.. ts:def:: TestingSetTimetravelRequest + + interface TestingSetTimetravelRequest { + // Offset added to the wallet's internal time, in milliseconds. + offsetMs: number; + } diff --git a/core/wallet-core/testing/testing-wait-balance.rst b/core/wallet-core/testing/testing-wait-balance.rst @@ -0,0 +1,23 @@ +.. ts:op:: testingWaitBalance + :read-only: + + Wait until a balance has reached the desired value. Waits until the + material balance in the currency of ``amount`` equals ``amount``; + only ``type`` ``"material"`` is currently supported. The operation + blocks until this condition is met or the wait fails, for example + when the request is cancelled. + + **Request:** + + The request must be a `TestingWaitBalanceRequest` object. + + **Response:** + + On success, the result is an empty object. + +.. ts:def:: TestingWaitBalanceRequest + + interface TestingWaitBalanceRequest { + type: "material" | "available"; + amount: AmountString; + } diff --git a/core/wallet-core/testing/testing-wait-exchange-ready.rst b/core/wallet-core/testing/testing-wait-exchange-ready.rst @@ -0,0 +1,34 @@ +.. ts:op:: testingWaitExchangeReady + + Wait until an exchange entry is ready. Returns an error if updating + the exchange failed. + + **Request:** + + The request must be a `TestingWaitExchangeReadyRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + May trigger an update of the exchange entry over the network, for + example when ``forceUpdate`` is set. + +.. ts:def:: TestingWaitExchangeReadyRequest + + interface TestingWaitExchangeReadyRequest { + exchangeBaseUrl: string; + + // Do not stop waiting even when the exchange is + // in an error state. + noBail?: boolean; + + // Force waiting until an update really happened. + forceUpdate?: boolean; + + // Only consider the exchange as ready if the + // next auto-refresh is scheduled for the future. + waitAutoRefresh?: boolean; + } diff --git a/core/wallet-core/testing/testing-wait-exchange-state.rst b/core/wallet-core/testing/testing-wait-exchange-state.rst @@ -0,0 +1,31 @@ +.. ts:op:: testingWaitExchangeState + :read-only: + + Wait until an exchange entry is in a particular state. Currently, + only the wallet KYC status of the exchange entry can be waited on. + The operation blocks until this condition is met or the wait fails, + for example when the request is cancelled. + + **Request:** + + The request must be a `TestingWaitExchangeStateRequest` object. + + **Response:** + + On success, the result is an empty object. + +.. ts:def:: TestingWaitExchangeStateRequest + + interface TestingWaitExchangeStateRequest { + exchangeBaseUrl: string; + walletKycStatus?: ExchangeWalletKycStatus; + } + +.. ts:def:: ExchangeWalletKycStatus + + type ExchangeWalletKycStatus = + | "done" + // Wallet needs to request KYC status. + | "legi-init" + // User requires KYC or AML. + | "legi"; diff --git a/core/wallet-core/testing/testing-wait-refreshes-final.rst b/core/wallet-core/testing/testing-wait-refreshes-final.rst @@ -0,0 +1,14 @@ +.. ts:op:: testingWaitRefreshesFinal + :read-only: + + Wait until all refresh transactions are in a final state. The + operation blocks until this condition is met or the wait fails, for + example when the request is cancelled. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. diff --git a/core/wallet-core/testing/testing-wait-tasks-done.rst b/core/wallet-core/testing/testing-wait-tasks-done.rst @@ -0,0 +1,14 @@ +.. ts:op:: testingWaitTasksDone + :read-only: + + Wait until all pending tasks of the wallet are done. The operation + blocks until this condition is met or the wait fails, for example + when the request is cancelled. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. diff --git a/core/wallet-core/testing/testing-wait-transaction-state.rst b/core/wallet-core/testing/testing-wait-transaction-state.rst @@ -0,0 +1,19 @@ +.. ts:op:: testingWaitTransactionState + :read-only: + + This operation is a legacy alias of :ts:op:`waitTransactionState`. + It waits until a transaction is in a particular state and has the + same behavior and payloads; the legacy type names + `TestingWaitTransactionRequest` and + `TestingWaitTransactionStateResponse` are aliases of + `WaitTransactionStateRequest` and `WaitTransactionStateResponse`, + respectively. The operation blocks until the transaction reaches the + target state or the wait fails or times out. + + **Request:** + + The request must be a `WaitTransactionStateRequest` object. + + **Response:** + + On success, the result is a `WaitTransactionStateResponse` object. diff --git a/core/wallet-core/testing/testing-wait-transactions-final.rst b/core/wallet-core/testing/testing-wait-transactions-final.rst @@ -0,0 +1,14 @@ +.. ts:op:: testingWaitTransactionsFinal + :read-only: + + Wait until all transactions are in a final state. The operation + blocks until this condition is met or the wait fails, for example + when the request is cancelled. + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is an empty object. diff --git a/core/wallet-core/testing/testing-wait-wallet-kyc.rst b/core/wallet-core/testing/testing-wait-wallet-kyc.rst @@ -0,0 +1,28 @@ +.. ts:op:: testingWaitWalletKyc + :read-only: + + Wait for the wallet KYC process at an exchange. With ``passed`` set + to true, waits until KYC has been passed for at least the given + ``amount``; otherwise already returns when legitimization is + required. The operation blocks until this condition is met or the + wait fails, for example when the request is cancelled. + + **Request:** + + The request must be a `TestingWaitWalletKycRequest` object. + + **Response:** + + On success, the result is an empty object. + +.. ts:def:: TestingWaitWalletKycRequest + + interface TestingWaitWalletKycRequest { + exchangeBaseUrl: string; + amount: AmountString; + + // Do we wait for the KYC to be passed (true), + // or do we already return if legitimization is + // required (false). + passed: boolean; + } diff --git a/core/wallet-core/testing/withdraw-test-balance.rst b/core/wallet-core/testing/withdraw-test-balance.rst @@ -0,0 +1,55 @@ +.. ts:op:: withdrawTestBalance + + Make a withdrawal on a test deployment of the exchange and corebank. + + **Request:** + + The request must be a `WithdrawTestBalanceRequest` object. + + **Response:** + + On success, the result is a `WithdrawTestBalanceResult` object. + + **Side effects:** + + Registers a random bank user and performs a real withdrawal at the + given test deployment over the network; creates a withdrawal + transaction in the wallet. + + **Details:** + + The operation registers a random bank user via the corebank API, + creates a withdrawal operation for the requested amount, accepts + the resulting withdrawal URI as a bank-integrated withdrawal at the + given exchange, and finally confirms the withdrawal operation at + the bank. + +.. ts:def:: WithdrawTestBalanceRequest + + interface WithdrawTestBalanceRequest { + // Amount to withdraw. + amount: AmountString; + + // Corebank API base URL. + corebankApiBaseUrl: string; + + // Exchange to use for withdrawal. + exchangeBaseUrl: string; + + // Force the usage of a particular denomination selection. + // Only useful for testing. + forcedDenomSel?: ForcedDenomSel; + + // If set to true, treat the account created during + // the withdrawal as a foreign withdrawal account. + useForeignAccount?: boolean; + } + +.. ts:def:: ForcedDenomSel + + interface ForcedDenomSel { + denoms: { + value: AmountString; + count: number; + }[]; + } diff --git a/core/wallet-core/testing/withdraw-testkudos.rst b/core/wallet-core/testing/withdraw-testkudos.rst @@ -0,0 +1,28 @@ +.. ts:op:: withdrawTestkudos + + Make a withdrawal of ``TESTKUDOS:10`` from the test deployment at + test.taler.net (exchange ``https://exchange.test.taler.net/`` via the + corebank API at ``https://bank.test.taler.net/``). + + **Request:** + + This operation takes no arguments (an empty object). + + **Response:** + + On success, the result is a `WithdrawTestBalanceResult` object. + + **Side effects:** + + Performs a real withdrawal at the test deployment over the network + and creates a withdrawal transaction in the wallet. + +.. ts:def:: WithdrawTestBalanceResult + + interface WithdrawTestBalanceResult { + // Transaction ID of the newly created withdrawal transaction. + transactionId: TransactionIdStr; + + // Account of the user registered for the withdrawal. + accountPaytoUri: string; + } diff --git a/core/wallet-core/tokens.rst b/core/wallet-core/tokens.rst @@ -1,185 +0,0 @@ -.. _wallet-op-listDiscounts: - -**listDiscounts** - -List discount tokens stored in the wallet. Listed tokens are grouped -by token family. Only tokens that are within their validity period -and not currently in use by a transaction are listed. - -**Request:** - - The request arguments must be a `ListDiscountsRequest` object. - Omitted filter fields do not restrict the result. - -**Response:** - - On success, the result is a `ListDiscountsResponse` object. - -.. ts:def:: ListDiscountsRequest - - interface ListDiscountsRequest { - // Filter by hash of token issue public key. - tokenIssuePubHash?: string; - - // Filter by merchant base URL. - merchantBaseUrl?: string; - } - -.. ts:def:: ListDiscountsResponse - - interface ListDiscountsResponse { - discounts: DiscountListDetail[]; - } - -.. ts:def:: DiscountListDetail - - interface DiscountListDetail { - // Hash of token family info. - tokenFamilyHash: string; - - // Hash of token issue public key. - tokenIssuePubHash: string; - - // URL of the merchant issuing the token. - merchantBaseUrl: string; - - // Information about the merchant issuing the token. - merchantInfo?: MerchantInfo; - - // Human-readable name for the token family. - name: string; - - // Human-readable description for the token family. - description: string; - - // Optional map from IETF BCP 47 language tags to localized descriptions. - descriptionI18n: any | undefined; - - // Start time of the token's validity period. - validityStart: Timestamp; - - // End time of the token's validity period. - validityEnd: Timestamp; - - // Number of tokens available to use. - tokensAvailable: number; - } - -.. ts:def:: MerchantInfo - - interface MerchantInfo { - // The merchant's legal name of business. - name: string; - - // Contact email address of the merchant. - email?: string; - - // Website of the merchant. - website?: string; - - // An optional base64-encoded product image. - logo?: ImageDataUrl; - - // Business address of the merchant. - address?: Location; - - // Jurisdiction for disputes; some typical location fields may be absent. - jurisdiction?: Location; - } - - -.. _wallet-op-deleteDiscount: - -**deleteDiscount** - -Delete all discount tokens of one token family from the wallet. - -**Request:** - - The request arguments must be a `DeleteDiscountRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TOKENS_IN_USE``. - -**Details:** - - The deletion fails with ``WALLET_TOKENS_IN_USE`` if any token of the - family is currently in use by a transaction. - -.. ts:def:: DeleteDiscountRequest - - interface DeleteDiscountRequest { - // Hash of token family info. - tokenFamilyHash: string; - } - - -.. _wallet-op-listSubscriptions: - -**listSubscriptions** - -List subscription tokens stored in the wallet. Listed tokens are -grouped by token family. Only tokens that are within their validity -period and not currently in use by a transaction are listed. - -**Request:** - - The request arguments must be a `ListSubscriptionsRequest` object, - which has the same fields as `ListDiscountsRequest`. - -**Response:** - - On success, the result is a `ListSubscriptionsResponse` object. - -.. ts:def:: ListSubscriptionsRequest - - type ListSubscriptionsRequest = ListDiscountsRequest; - -.. ts:def:: ListSubscriptionsResponse - - interface ListSubscriptionsResponse { - subscriptions: SubscriptionListDetail[]; - } - -.. ts:def:: SubscriptionListDetail - - // Same as DiscountListDetail, but without a token count. - type SubscriptionListDetail = Omit<DiscountListDetail, "tokensAvailable">; - - -.. _wallet-op-deleteSubscription: - -**deleteSubscription** - -Delete all subscription tokens of one token family from the wallet. - -**Request:** - - The request arguments must be a `DeleteSubscriptionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TOKENS_IN_USE``. - -**Details:** - - The deletion fails with ``WALLET_TOKENS_IN_USE`` if any token of the - family is currently in use by a transaction. - -.. ts:def:: DeleteSubscriptionRequest - - interface DeleteSubscriptionRequest { - // Hash of token family info. - tokenFamilyHash: string; - } diff --git a/core/wallet-core/tokens/delete-discount.rst b/core/wallet-core/tokens/delete-discount.rst @@ -0,0 +1,29 @@ +.. ts:op:: deleteDiscount + + Delete all discount tokens of one token family from the wallet. + + **Request:** + + The request arguments must be a `DeleteDiscountRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes all discount tokens of the token family from the wallet + database. If any token of the family is still in use by a + transaction, nothing is deleted. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TOKENS_IN_USE``. + +.. ts:def:: DeleteDiscountRequest + + interface DeleteDiscountRequest { + // Hash of token family info. + tokenFamilyHash: string; + } diff --git a/core/wallet-core/tokens/delete-subscription.rst b/core/wallet-core/tokens/delete-subscription.rst @@ -0,0 +1,29 @@ +.. ts:op:: deleteSubscription + + Delete all subscription tokens of one token family from the wallet. + + **Request:** + + The request arguments must be a `DeleteSubscriptionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes all subscription tokens of the token family from the wallet + database. If any token of the family is still in use by a + transaction, nothing is deleted. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TOKENS_IN_USE``. + +.. ts:def:: DeleteSubscriptionRequest + + interface DeleteSubscriptionRequest { + // Hash of token family info. + tokenFamilyHash: string; + } diff --git a/core/wallet-core/tokens/list-discounts.rst b/core/wallet-core/tokens/list-discounts.rst @@ -0,0 +1,87 @@ +.. ts:op:: listDiscounts + :read-only: + + List discount tokens stored in the wallet. Listed tokens are grouped + by token family. Only tokens that are within their validity period + and not currently in use by a transaction are listed. + + **Request:** + + The request arguments must be a `ListDiscountsRequest` object. + Omitted filter fields do not restrict the result. + + **Response:** + + On success, the result is a `ListDiscountsResponse` object. + +.. ts:def:: ListDiscountsRequest + + interface ListDiscountsRequest { + // Filter by hash of token issue public key. + tokenIssuePubHash?: string; + + // Filter by merchant base URL. + merchantBaseUrl?: string; + } + +.. ts:def:: ListDiscountsResponse + + interface ListDiscountsResponse { + discounts: DiscountListDetail[]; + } + +.. ts:def:: DiscountListDetail + + interface DiscountListDetail { + // Hash of token family info. + tokenFamilyHash: string; + + // Hash of token issue public key. + tokenIssuePubHash: string; + + // URL of the merchant issuing the token. + merchantBaseUrl: string; + + // Information about the merchant issuing the token. + merchantInfo?: MerchantInfo; + + // Human-readable name for the token family. + name: string; + + // Human-readable description for the token family. + description: string; + + // Optional map from IETF BCP 47 language tags to localized descriptions. + descriptionI18n: any | undefined; + + // Start time of the token's validity period. + validityStart: Timestamp; + + // End time of the token's validity period. + validityEnd: Timestamp; + + // Number of tokens available to use. + tokensAvailable: number; + } + +.. ts:def:: MerchantInfo + + interface MerchantInfo { + // The merchant's legal name of business. + name: string; + + // Contact email address of the merchant. + email?: string; + + // Website of the merchant. + website?: string; + + // An optional base64-encoded product image. + logo?: ImageDataUrl; + + // Business address of the merchant. + address?: Location; + + // Jurisdiction for disputes; some typical location fields may be absent. + jurisdiction?: Location; + } diff --git a/core/wallet-core/tokens/list-subscriptions.rst b/core/wallet-core/tokens/list-subscriptions.rst @@ -0,0 +1,30 @@ +.. ts:op:: listSubscriptions + :read-only: + + List subscription tokens stored in the wallet. Listed tokens are + grouped by token family. Only tokens that are within their validity + period and not currently in use by a transaction are listed. + + **Request:** + + The request arguments must be a `ListSubscriptionsRequest` object, + which has the same fields as `ListDiscountsRequest`. + + **Response:** + + On success, the result is a `ListSubscriptionsResponse` object. + +.. ts:def:: ListSubscriptionsRequest + + type ListSubscriptionsRequest = ListDiscountsRequest; + +.. ts:def:: ListSubscriptionsResponse + + interface ListSubscriptionsResponse { + subscriptions: SubscriptionListDetail[]; + } + +.. ts:def:: SubscriptionListDetail + + // Same as DiscountListDetail, but without a token count. + type SubscriptionListDetail = Omit<DiscountListDetail, "tokensAvailable">; diff --git a/core/wallet-core/transactions.rst b/core/wallet-core/transactions.rst @@ -1,1380 +0,0 @@ -.. _wallet-op-getTransactions: - -**getTransactions** - -Get the wallet's transaction list, containing past and pending -transactions. - -**Request:** - - The request ``args`` must be a `TransactionsRequest` object. - -**Response:** - - On success, the result is a `TransactionsResponse` object. - -**Details:** - - Refresh transactions are excluded from the result unless - ``includeRefreshes`` is set. - - With the default sort orders (``ascending`` and ``descending``), - pending transactions (major states ``pending``, ``aborting`` and - ``dialog``) are listed before all other transactions; within each - group, transactions are sorted by their timestamp, and ties are - broken by a fixed transaction-type order. With - ``stable-ascending``, all transactions are sorted purely by - ascending timestamp, so pending transactions do not jump around. - - Filtering by an auditor scope (``scopeInfo`` of type ``auditor``) - is not supported and fails with ``WALLET_CORE_API_BAD_REQUEST``. - -.. ts:def:: TransactionsRequest - - interface TransactionsRequest { - // Return only transactions in the given currency. - // Deprecated: use ``scopeInfo`` instead. - currency?: string; - - // Return only transactions in the given scope. - scopeInfo?: ScopeInfo; - - // Limit results to transactions related to the given search - // string. Currently accepted but not applied by wallet-core. - search?: string; - - // Sort order of the transaction items. "ascending" (the - // default) and "descending" sort by timestamp but list pending - // transactions first; "stable-ascending" sorts purely by - // ascending timestamp, with pending transactions in between. - sort?: "ascending" | "descending" | "stable-ascending"; - - // If true, include all refreshes in the transaction list. - includeRefreshes?: boolean; - - // If set, only return transactions matching the state filter. - filterByState?: TransactionStateFilter; - } - - -.. ts:def:: TransactionStateFilter - - // State filter for the transaction list: only transactions in a - // non-final state. - type TransactionStateFilter = "nonfinal"; - - -.. ts:def:: TransactionsResponse - - interface TransactionsResponse { - // List of past and pending transactions matching the request; - // see the respective operation for the sort order. - transactions: Transaction[]; - } - - -.. ts:def:: Transaction - - // A transaction in the wallet's transaction history. The - // ``type`` field discriminates the union; all members share the - // fields of `TransactionCommon`. - type Transaction = - | TransactionWithdrawal - | TransactionPayment - | TransactionRefund - | TransactionRefresh - | TransactionDeposit - | TransactionPeerPullCredit - | TransactionPeerPullDebit - | TransactionPeerPushCredit - | TransactionPeerPushDebit - | TransactionInternalWithdrawal - | TransactionRecoup - | TransactionDenomLoss; - - -.. ts:def:: TransactionIdStr - - // Opaque, stable identifier of a transaction, of the form - // ``txn:<type>:<id>`` where ``<type>`` is a `TransactionType`. - // (The TypeScript source additionally brands this string type; - // the brand only exists at compile time.) - type TransactionIdStr = `txn:${string}:${string}`; - - -.. ts:def:: TransactionType - - type TransactionType = - | "withdrawal" - | "internal-withdrawal" - | "payment" - | "refund" - | "refresh" - | "deposit" - | "peer-push-debit" - | "peer-push-credit" - | "peer-pull-debit" - | "peer-pull-credit" - | "recoup" - | "denom-loss"; - - -.. ts:def:: TransactionCommon - - interface TransactionCommon { - // Opaque unique ID for the transaction, used as a starting - // point for paginating queries and for invoking actions on the - // transaction (e.g. deleting it from the history). - transactionId: TransactionIdStr; - - // The transaction produced funds under an exchange key set that - // the user subsequently purged from the wallet. - legacy?: boolean; - - // Short identifier assigned by this wallet for local, - // human-facing use, of the form ``#<type>:<localIdent>``. - // Intentionally not portable: importing or merging a wallet can - // assign different local identifiers. Clients must use - // ``transactionId`` when they need a stable ID. Undefined when - // the wallet backend does not support local IDs. - localTransactionId?: string; - - // Type of the transaction; discriminates the `Transaction` - // union. - type: TransactionType; - - // Main timestamp of the transaction. - timestamp: TalerPreciseTimestamp; - - // Scopes of this transaction. - scopes: ScopeInfo[]; - - // Transaction state, as per DD37. - txState: TransactionState; - - // Wallet-internal state ID, only used for debugging and - // testing. - stId: number; - - // Possible transitions based on the current state. - txActions: TransactionAction[]; - - // Raw amount of the transaction (exclusive of fees or other - // extra costs). - amountRaw: AmountString; - - // Amount shown when the transaction was confirmed, including - // estimated fees. Preserved when execution fails, expires or - // is aborted. - amountEffective: AmountString; - - // Settled wallet balance effect, including fees and abort - // recovery. Nonnegative; the transaction type determines - // whether this is a debit or a credit. Absent until the - // transaction and its recovery have settled, or when the amount - // cannot be established for historical records. Ordinary - // merchant refunds remain separate credits. Associated - // refreshes have zero effect. - amountEffectiveFinal?: AmountString; - - error?: TalerErrorDetail; - - abortReason?: TalerErrorDetail; - - failReason?: TalerErrorDetail; - - // Location where the user must go to complete KYC; present - // when the transaction's minor state is ``kyc``. - kycUrl?: string; - - // KYC payto hash. Useful for testing, not so useful for UIs. - kycPaytoHash?: string; - - // KYC access token. Useful for testing, not so useful for UIs. - kycAccessToken?: string; - - kycAuthTransferInfo?: KycAuthTransferInfo; - } - - -.. ts:def:: TransactionState - - interface TransactionState { - // Major state component of the transaction state. - major: TransactionMajorState; - - // Minor state component of the transaction. - minor?: TransactionMinorState; - - // Whether the wallet is currently actively processing the - // transaction or waiting for a counterparty. Will eventually - // be folded into a new major state. - working?: boolean; - } - - -.. ts:def:: TransactionMajorState - - type TransactionMajorState = - // No state, only used when reporting transitions into the - // initial state. - | "none" - | "pending" - | "done" - | "aborting" - | "aborted" - | "dialog" - | "finalizing" - // A suspended pending state. - | "suspended" - | "suspended-finalizing" - | "suspended-aborting" - | "failed" - | "expired" - // Only used for notifications, never in the transaction - // history. - | "deleted"; - - -.. ts:def:: TransactionMinorState - - type TransactionMinorState = - | "aborting-bank" - | "accept-refund" - | "auto-refund" - | "balance-kyc" - | "bank" - | "bank-confirm-transfer" - | "bank-register-reserve" - | "check-refund" - | "claim-proposal" - | "completed-by-other-wallet" - | "continued-with-other-wallet" - | "create-purse" - | "delete-purse" - | "deposit" - | "deposit-abort-partial" - | "deposit-abort-recovered" - | "deposit-abort-recovery-failed" - | "deposit-abort-refund-failed" - | "deposit-abort-too-late" - | "exchange" - | "exchange-wait-reserve" - | "kyc-auth" - | "kyc-hard-limit" - | "kyc-init" - | "kyc" - | "merge" - | "paid-by-other" - | "proposed" - | "ready" - | "rebind-session" - | "refresh" - | "refused" - | "repurchase" - | "submit-payment" - | "track" - | "unknown" - | "withdraw" - | "waiting-for-other-wallet" - | "abort"; - - -.. ts:def:: TransactionAction - - // Actions the user can request on a transaction in its current - // state; each action corresponds to one of the transaction - // operations. - type TransactionAction = - | "delete" - | "suspend" - | "resume" - | "abort" - | "fail" - | "retry"; - - -.. ts:def:: KycAuthTransferInfo - - interface KycAuthTransferInfo { - // Payto URI of the account that must make the transfer. The - // KYC auth transfer will *not* work if it originates from a - // different account. - debitPaytoUri: string; - - // Account public key. Included in the transfer subject for - // some of the transfer options. - accountPub: string; - - // Options for making the KYC auth transfer, grouped by exchange - // credit account in the same format used for withdrawals. - transferOptionsExt: WithdrawalExchangeAccountDetails[]; - - // Options for making the KYC auth transfer payment to the - // exchange. Deprecated: use ``transferOptionsExt`` instead. - transferOptions: TransferOption[]; - - // Validity of the transferOptions, or undefined if they do not - // expire. Deprecated: use the per-account expiry in - // ``transferOptionsExt`` instead. - transferExpiry: TalerProtocolTimestamp | undefined; - - // Amount that the exchange expects to be deposited. Usually - // the smallest amount that can be transferred via a bank - // transfer. Deprecated: use ``transferOptions`` instead. - amount: AmountString; - - // Possible target payto URIs. Deprecated: use - // ``transferOptions`` instead. - creditPaytoUris: string[]; - } - - -.. ts:def:: TransactionWithdrawal - - // A withdrawal transaction (either bank-integrated or manual). - interface TransactionWithdrawal extends TransactionCommon { - type: "withdrawal"; - - // Exchange of the withdrawal. - exchangeBaseUrl: string | undefined; - - // Amount that got subtracted from the reserve balance. - amountRaw: AmountString; - - // Amount that actually was (or will be) added to the wallet's - // balance. - amountEffective: AmountString; - - withdrawalDetails: WithdrawalDetails; - } - - -.. ts:def:: TransactionInternalWithdrawal - - // Internal withdrawal operation, only reported on request. Some - // transactions (peer-*-credit) internally do a withdrawal, but - // only the peer-*-credit transaction is reported. The internal - // withdrawal transaction gives access to the details of the - // underlying withdrawal for testing/debugging. It is usually not - // reported, so that the amounts of transactions properly add up. - interface TransactionInternalWithdrawal extends TransactionCommon { - type: "internal-withdrawal"; - - // Exchange of the withdrawal. - exchangeBaseUrl: string; - - // Amount that got subtracted from the reserve balance. - amountRaw: AmountString; - - // Amount that actually was (or will be) added to the wallet's - // balance. - amountEffective: AmountString; - - withdrawalDetails: WithdrawalDetails; - } - - -.. ts:def:: WithdrawalDetails - - type WithdrawalDetails = - | WithdrawalDetailsForManualTransfer - | WithdrawalDetailsForTalerBankIntegrationApi; - - -.. ts:def:: WithdrawalType - - type WithdrawalType = - | "taler-bank-integration-api" - | "manual-transfer"; - - -.. ts:def:: WithdrawalDetailsForManualTransfer - - interface WithdrawalDetailsForManualTransfer { - type: "manual-transfer"; - - // Payto URIs that the exchange supports. Already contains the - // amount and message. Deprecated: in favor of - // ``exchangeCreditAccountDetails``. - exchangePaytoUris: string[]; - - exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[]; - - // Public key of the reserve. - reservePub: string; - - // Is the reserve ready for withdrawal? - reserveIsReady: boolean; - - // How long the exchange waits to transfer back funds from a - // reserve. - reserveClosingDelay: TalerProtocolDuration; - } - - -.. ts:def:: WithdrawalDetailsForTalerBankIntegrationApi - - interface WithdrawalDetailsForTalerBankIntegrationApi { - type: "taler-bank-integration-api"; - - // True if the bank has confirmed the withdrawal. An - // unconfirmed withdrawal usually requires user input and should - // be highlighted in the UI; see ``bankConfirmationUrl``. - confirmed: boolean; - - // If the withdrawal is unconfirmed, this can include a URL for - // user-initiated confirmation. - bankConfirmationUrl?: string; - - // Public key of the reserve. - reservePub: string; - - // Is the reserve ready for withdrawal? - reserveIsReady: boolean; - - // Is the bank transfer for the withdrawal externally - // confirmed? - externalConfirmation?: boolean; - - exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[]; - } - - -.. ts:def:: TransactionPayment - - interface TransactionPayment extends TransactionCommon { - type: "payment"; - - // Merchant instance base URL used to claim the order. - // Available even before the contract terms have been - // downloaded. - merchantBaseUrl: string; - - // Public payment URI shown while this wallet waits for another - // wallet to claim an order that it released. - unclaimedPayUri?: TalerUriString; - - // Additional information about the payment. Only present if - // the information about the order is already available. - info: OrderShortInfo | undefined; - - // Full contract terms. Only included if explicitly requested - // via the ``includeContractTerms`` flag of - // ``getTransactionById``. - contractTerms?: MerchantContractTerms; - - // Amount that must be paid for the contract. - amountRaw: AmountString; - - // Amount that was paid, including deposit, wire and refresh - // fees. - amountEffective: AmountString; - - // Amount that has been refunded by the merchant. - totalRefundRaw: AmountString; - - // Amount that will be added to the wallet's balance after fees - // and refreshing. - totalRefundEffective: AmountString; - - // Amount pending to be picked up. - refundPending: AmountString | undefined; - - // Reference to applied refunds. - refunds: RefundInfoShort[]; - - // Is the wallet currently checking for a refund? - refundQueryActive: boolean; - - // PoS confirmation codes, separated by newlines. Only present - // for purchases that support PoS confirmation. - posConfirmation: string | undefined; - - // Until when the ``posConfirmation`` is valid. - posConfirmationDeadline?: TalerProtocolTimestamp; - - // Did we receive the payment via a taler://pay-template/ URI - // and did the URI contain a nfc=1 flag? - posConfirmationViaNfc?: boolean; - - // In case this payment transaction was detected as a - // repurchase, the transaction ID of the original payment. - repurchaseTransactionId?: TransactionIdStr; - - // If applicable, the choice that the user selected. - choiceIndex?: number; - } - - -.. ts:def:: OrderShortInfo - - interface OrderShortInfo { - // Order ID, uniquely identifies the order within a merchant - // instance. - orderId: string; - - // Hash of the contract terms. - contractTermsHash: string; - - // More information about the merchant. - merchant: MerchantInfo; - - // Summary of the order, given by the merchant. - summary: string; - - // Map from IETF BCP 47 language tags to localized summaries. - summary_i18n?: InternationalizedString; - - // URL of the fulfillment, given by the merchant. - fulfillmentUrl?: string; - - // Plain text message that should be shown to the user when the - // payment is complete. - fulfillmentMessage?: string; - - // Translations of ``fulfillmentMessage``. - fulfillmentMessage_i18n?: InternationalizedString; - } - - -.. ts:def:: RefundInfoShort - - interface RefundInfoShort { - transactionId: string; - - timestamp: TalerProtocolTimestamp; - - amountEffective: AmountString; - - amountRaw: AmountString; - } - - -.. ts:def:: TransactionRefund - - interface TransactionRefund extends TransactionCommon { - // Recovery already included in the unsuccessful payment's - // final cost. - isAbortRecovery?: boolean; - - type: "refund"; - - // Amount that has been refunded by the merchant. - amountRaw: AmountString; - - // Amount that will be added to the wallet's balance after fees - // and refreshing. - amountEffective: AmountString; - - // ID of the transaction that is refunded. - refundedTransactionId: string; - - paymentInfo: RefundPaymentInfo | undefined; - } - - -.. ts:def:: RefundPaymentInfo - - // Summary information about the payment that we got a refund - // for. - interface RefundPaymentInfo { - summary: string; - - summary_i18n?: InternationalizedString; - - // More information about the merchant. - merchant: MerchantInfo; - } - - -.. ts:def:: TransactionRefresh - - // A transaction shown for refreshes. Only shown for (1) - // refreshes not associated with other transactions and (2) - // refreshes in an error state. - interface TransactionRefresh extends TransactionCommon { - type: "refresh"; - - refreshReason: RefreshReason; - - // Transaction ID that caused this refresh. - originatingTransactionId?: string; - - // Always zero for refreshes. - amountRaw: AmountString; - - // Fees, i.e. the effective, negative effect of the refresh on - // the balance. Only applicable for stand-alone refreshes, and - // zero for other refreshes where the transaction itself - // accounts for the refresh fee. - amountEffective: AmountString; - - refreshInputAmount: AmountString; - - refreshOutputAmount: AmountString; - } - - -.. ts:def:: RefreshReason - - // Reason why a coin is being refreshed. - type RefreshReason = - | "manual" - | "pay-merchant" - | "pay-deposit" - | "pay-peer-push" - | "pay-peer-pull" - | "refund" - | "abort-pay" - | "abort-deposit" - | "abort-peer-push-debit" - | "abort-peer-pull-debit" - | "recoup" - | "backup-restored" - | "scheduled"; - - -.. ts:def:: TransactionDeposit - - // Deposit transaction, which effectively sends money from this - // wallet somewhere else. - interface TransactionDeposit extends TransactionCommon { - type: "deposit"; - - depositGroupId: string; - - // Target for the deposit. - targetPaytoUri: string; - - // Raw amount that is being deposited. - amountRaw: AmountString; - - // Deposit account public key. - accountPub: string; - - // Effective amount that is being deposited. - amountEffective: AmountString; - - wireTransferDeadline: TalerProtocolTimestamp; - - wireTransferProgress: number; - - // Did all the deposit requests succeed? - deposited: boolean; - - trackingState: Array<DepositTransactionTrackingState>; - } - - -.. ts:def:: DepositTransactionTrackingState - - interface DepositTransactionTrackingState { - // Raw wire transfer identifier of the deposit. - wireTransferId: string; - - // When the wire transfer was given to the bank. - timestampExecuted: TalerProtocolTimestamp; - - // Total amount transferred for this wtid (including fees). - amountRaw: AmountString; - - // Wire fee amount for this exchange. - wireFee: AmountString; - } - - -.. ts:def:: TransactionPeerPullCredit - - // Credit because we were paid for a P2P invoice we created. - interface TransactionPeerPullCredit extends TransactionCommon { - type: "peer-pull-credit"; - - info: PeerInfoShort; - - // Exchange used. - exchangeBaseUrl: string; - - // Amount that got subtracted from the reserve balance. - amountRaw: AmountString; - - // Amount that actually was (or will be) added to the wallet's - // balance. - amountEffective: AmountString; - - // URI to send to the other party. Only available in the right - // state. - talerUri: string | undefined; - } - - -.. ts:def:: TransactionPeerPullDebit - - // Debit because we paid someone's invoice. - interface TransactionPeerPullDebit extends TransactionCommon { - type: "peer-pull-debit"; - - info: PeerInfoShort; - - // Exchange used. - exchangeBaseUrl: string; - - amountRaw: AmountString; - - amountEffective: AmountString; - } - - -.. ts:def:: TransactionPeerPushDebit - - // We sent money via a P2P payment. - interface TransactionPeerPushDebit extends TransactionCommon { - type: "peer-push-debit"; - - info: PeerInfoShort; - - // Exchange used. - exchangeBaseUrl: string; - - // Amount that got subtracted from the reserve balance. - amountRaw: AmountString; - - // Amount that actually was (or will be) added to the wallet's - // balance. - amountEffective: AmountString; - - // URI to accept the payment. Only present if the transaction - // is in a state where the other party can accept the payment. - talerUri?: string; - } - - -.. ts:def:: TransactionPeerPushCredit - - // We received money via a P2P payment. - interface TransactionPeerPushCredit extends TransactionCommon { - type: "peer-push-credit"; - - info: PeerInfoShort; - - // Exchange used. - exchangeBaseUrl: string; - - // Amount that got subtracted from the reserve balance. - amountRaw: AmountString; - - // Amount that actually was (or will be) added to the wallet's - // balance. - amountEffective: AmountString; - } - - -.. ts:def:: PeerInfoShort - - interface PeerInfoShort { - expiration: TalerProtocolTimestamp | undefined; - - summary: string | undefined; - - iconId: string | undefined; - } - - -.. ts:def:: TransactionRecoup - - // The exchange revoked a key and the wallet recoups funds. - interface TransactionRecoup extends TransactionCommon { - type: "recoup"; - } - - -.. ts:def:: TransactionDenomLoss - - // A transaction to indicate financial loss due to denominations - // that became unusable for deposits. - interface TransactionDenomLoss extends TransactionCommon { - type: "denom-loss"; - - lossEventType: DenomLossEventType; - - exchangeBaseUrl: string; - } - - -.. ts:def:: DenomLossEventType - - type DenomLossEventType = - | "denom-expired" - | "denom-vanished" - | "denom-unoffered" - // The exchange revoked the denomination. A revoked - // denomination also stops being offered, so this must be - // checked before "denom-unoffered" to say what actually - // happened. - | "denom-revoked"; - - -.. _wallet-op-getTransactionsV2: - -**getTransactionsV2** - -Get the wallet's transaction list, with support for paginated -queries. - -**Request:** - - The request ``args`` must be a `GetTransactionsV2Request` object. - -**Response:** - - On success, the result is a `TransactionsResponse` object. - -**Details:** - - Without a ``limit`` and without an offset, all matching - transactions are returned in ascending timestamp order. - - With a positive ``limit``, at most ``limit`` transactions are - returned in ascending order: without an offset starting at the - first transaction, with an offset starting after the offset. With - a negative ``limit``, at most ``-limit`` transactions are returned - in descending order: without an offset starting at the last - transaction, with an offset starting before the offset. - Transactions with equal timestamps are ordered by their - ``transactionId``. - - If the ``offsetTransactionId`` no longer exists (for example - because the transaction was deleted), ``offsetTimestamp`` is used - as a fallback anchor. If the offset transaction does not exist - and no ``offsetTimestamp`` is given, the request fails with - ``WALLET_TRANSACTION_NOT_FOUND``. - - Refresh transactions are excluded unless ``includeRefreshes`` (or - ``includeAll``) is set; payments that were superseded by a - repurchase are excluded unless ``includeAll`` is set. - -.. ts:def:: GetTransactionsV2Request - - interface GetTransactionsV2Request { - // Return only transactions in the given currency. - currency?: string; - - // Return only transactions in the given scope. - scopeInfo?: ScopeInfo; - - // If true, include all refreshes in the transaction list. - includeRefreshes?: boolean; - - // If true, include transactions that would usually be filtered - // out. Implies ``includeRefreshes``. - includeAll?: boolean; - - // Only return transactions before/after this offset. - offsetTransactionId?: TransactionIdStr; - - // Only return transactions before/after the transaction with - // this timestamp. Used as a fallback if the - // ``offsetTransactionId`` was deleted. - offsetTimestamp?: TalerPreciseTimestamp; - - // Number of transactions to return. When positive, results are - // returned in ascending timestamp order (starting at the first - // transaction or after the offset). When negative, results - // are returned in descending timestamp order (starting at the - // last transaction or before the offset). - limit?: number; - - // Filter transactions by their state / state category. - // If not specified, all transactions are returned. - // "final": transactions in any final state; - // "nonfinal": transactions in any state but the final states; - // "nonfinal-dialog": nonfinal transactions that require - // confirmation / some choice by the user; - // "nonfinal-approved": nonfinal transactions that need no - // further user approval; - // "done": transactions in the "done" major state. - filterByState?: - | "final" - | "nonfinal" - | "done" - | "nonfinal-approved" - | "nonfinal-dialog"; - } - - -.. _wallet-op-getTransactionById: - -**getTransactionById** - -Get a single transaction by its identifier. - -**Request:** - - The request ``args`` must be a `TransactionByIdRequest` object. - -**Response:** - - On success, the result is a `Transaction` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - The ``transactionId`` must have the form of a `TransactionIdStr`; - malformed identifiers fail with ``WALLET_CORE_API_BAD_REQUEST``, - well-formed but unknown identifiers with - ``WALLET_TRANSACTION_NOT_FOUND``. - - The full contract terms (``contractTerms``) are only reported for - payment transactions and only when ``includeContractTerms`` is - set. - -.. ts:def:: TransactionByIdRequest - - interface TransactionByIdRequest { - transactionId: string; - - // If set to true, report the full contract terms in the - // response if the transaction has them. - includeContractTerms?: boolean; - } - - -.. _wallet-op-resolveTransactionReference: - -**resolveTransactionReference** - -Resolve a stable transaction ID, a wallet-local identifier, or an -external withdrawal reference to a stable transaction ID. - -**Request:** - - The request ``args`` must be a `ResolveTransactionReferenceRequest` - object. - -**Response:** - - On success, the result is a `ResolveTransactionReferenceResponse` - object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. - -**Details:** - - Three kinds of references are accepted: - - * a stable transaction ID (`TransactionIdStr`), which is returned - unchanged; - * a wallet-local identifier of the form ``#<type>:<localIdent>``, - as reported in the transaction's ``localTransactionId`` field. - Local identifiers are deliberately not portable: importing or - merging a wallet can assign different local identifiers; - * an external, bank-generated withdrawal reference (such as an - LSD 0006 withdrawal-transfer-result reference) that contains - the reserve public key of a withdrawal. The reference must - match exactly one withdrawal transaction, otherwise the request - fails with ``WALLET_TRANSACTION_NOT_FOUND``. - -.. ts:def:: ResolveTransactionReferenceRequest - - interface ResolveTransactionReferenceRequest { - transactionReference: string; - } - - -.. ts:def:: ResolveTransactionReferenceResponse - - interface ResolveTransactionReferenceResponse { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-abortTransaction: - -**abortTransaction** - -Abort a transaction. - -**Request:** - - The request ``args`` must be an `AbortTransactionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. - -**Details:** - - For payment transactions, aborting puts the payment into an - ``aborting`` state, in which the wallet recovers the spent coins - via a refund where possible before the transaction reaches a - final state. Whether a transaction can currently be aborted is - advertised in its ``txActions``. - -.. ts:def:: AbortTransactionRequest - - interface AbortTransactionRequest { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-failTransaction: - -**failTransaction** - -Mark a transaction as failed, giving up on its ongoing processing. - -**Request:** - - The request ``args`` must be a `FailTransactionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. - -**Details:** - - This is typically used for transactions in an ``aborting`` state - (for example while recovering an aborted payment via a refund): - the user stops waiting for the recovery, and the transaction - transitions to the final ``failed`` state. The reason is - recorded in the transaction's ``failReason``. Whether a - transaction can currently be failed is advertised in its - ``txActions``. - -.. ts:def:: FailTransactionRequest - - interface FailTransactionRequest { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-suspendTransaction: - -**suspendTransaction** - -Suspend a transaction, stopping any associated network activities -while keeping the option of trying again at a later time. This can -be useful to save battery power or bandwidth when an operation is -expected to take longer, such as a very large withdrawal. - -**Request:** - - The request ``args`` must be an `AbortTransactionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. - - -.. _wallet-op-resumeTransaction: - -**resumeTransaction** - -Resume a transaction that was previously suspended with -:ref:`suspendTransaction <wallet-op-suspendTransaction>`. - -**Request:** - - The request ``args`` must be an `AbortTransactionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. - - -.. _wallet-op-deleteTransaction: - -**deleteTransaction** - -Permanently delete a transaction from the wallet's transaction -history. - -**Request:** - - The request ``args`` must be a `DeleteTransactionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``, - ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. - -**Details:** - - Deleting is only possible in states that advertise the ``delete`` - action (in ``txActions``), typically final or dialog states. Any - background task associated with the transaction is stopped. - -.. ts:def:: DeleteTransactionRequest - - interface DeleteTransactionRequest { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-retryTransaction: - -**retryTransaction** - -Immediately retry the transaction's underlying operation by -resetting the retry timer of its background task. - -**Request:** - - The request ``args`` must be a `RetryTransactionRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TRANSACTION_NOT_FOUND``. - -**Details:** - - Retrying is only possible in states that advertise the ``retry`` - action (in ``txActions``); the request does not wait for the - retried operation to complete. - -.. ts:def:: RetryTransactionRequest - - interface RetryTransactionRequest { - transactionId: TransactionIdStr; - } - - -.. _wallet-op-listAssociatedRefreshes: - -**listAssociatedRefreshes** - -List the refresh transactions associated with another transaction. - -**Request:** - - The request ``args`` must be a `ListAssociatedRefreshesRequest` - object. - -**Response:** - - On success, the result is a `ListAssociatedRefreshesResponse` - object. - -**Details:** - - This operation is declared but not implemented yet; it currently - always fails with ``GENERIC_FEATURE_NOT_IMPLEMENTED``. - -.. ts:def:: ListAssociatedRefreshesRequest - - interface ListAssociatedRefreshesRequest { - transactionId: string; - } - - -.. ts:def:: ListAssociatedRefreshesResponse - - interface ListAssociatedRefreshesResponse { - transactionIds: string[]; - } - - -.. _wallet-op-waitTransactionState: - -**waitTransactionState** - -Wait until a transaction is in a particular state. - -**Request:** - - The request ``args`` must be a `WaitTransactionStateRequest` - object. - -**Response:** - - On success, the result is a `WaitTransactionStateResponse` - object. - -**Details:** - - This is a long-polling operation: it returns once the - transaction's state matches ``txState``, matches one of the - ``bailStates``, or (with ``bailOnError``) an error is recorded - for the transaction. The response's ``matched`` field says which - of the two sets of states ended the wait. - - The ``txState`` and ``bailStates`` fields are - `TestingWaitTxStateSpec` values: a `TransactionStatePattern` or a - list of patterns (matching when any pattern matches), a - wallet-internal numeric state ID, or one of the shorthands - ``nonpending`` (any major state other than ``pending``) and - ``final`` (any final major state). In a pattern, ``major``, - ``minor`` and ``working`` accept the wildcard ``*``; a pattern - without ``minor`` only matches states that have no minor state, - while a pattern without ``working`` matches regardless of the - flag. - - Without ``bailStates`` or ``bailOnError``, a transaction that - reaches a state it will never leave keeps the caller waiting - until the ``timeout`` expires; the wait then fails with - ``GENERIC_TIMEOUT``. - - When ``progressToken`` is set, the wait reports request progress - notifications (including recorded transaction errors with their - retry counter and remaining retry delay) and can be cancelled - with :ref:`cancelProgressToken <wallet-op-cancelProgressToken>` - or nudged with - :ref:`retryProgressTokenNow <wallet-op-retryProgressTokenNow>`. - Cancellation stops only the wait, not the transaction. - -.. ts:def:: WaitTransactionStateRequest - - interface WaitTransactionStateRequest { - transactionId: TransactionIdStr; - - // Receive request progress notifications and control this wait - // via ``cancelProgressToken``/``retryProgressTokenNow``. - // Cancellation stops only the wait. Retry-now retries the - // transaction if its current state allows it, without - // restarting the wait or extending its timeout. Transaction - // errors are reported with their recorded retry counter and - // remaining delay; without a recorded error retry, the counter - // is zero and the delay is "forever". - progressToken?: string; - - // Additional identifier that is used in the logs to easily - // find the status of the particular wait request. - logId?: string; - - // After the timeout has passed, give up on waiting for the - // desired state and raise an error instead. - timeout?: DurationUnitSpec; - - // If set to true, wait until the desired state is reached - // with an error. - requireError?: boolean; - - // State(s) to wait for. - txState: TestingWaitTxStateSpec; - - // States that end the wait even though they are not the state - // that was waited for. The response says which of the two - // sets matched. Without this, a transaction that ends up in - // a state it will never leave keeps the caller waiting until - // the timeout. - bailStates?: TestingWaitTxStateSpec; - - // End the wait as soon as an error is recorded for the - // transaction. Beware that this includes transient errors of - // retried operations, which are cleared again once the - // operation succeeds. - bailOnError?: boolean; - } - - -.. ts:def:: WaitTransactionStateResponse - - interface WaitTransactionStateResponse { - // Which set of states ended the wait: the requested state or - // one of the bail states. - matched: "target" | "bail"; - - // State that ended the wait. - txState: TransactionState; - - // Wallet-internal state ID, only used for debugging and - // testing. - stId: number; - } - - -.. ts:def:: TestingWaitTxStateSpec - - // State(s) to wait for: a plain pattern or a list of patterns - // (matching any of them), a wallet-internal numeric state ID, or - // one of the shorthands for a category of states. - type TestingWaitTxStateSpec = - | TransactionStatePattern - | TransactionStatePattern[] - | number - | "nonpending" - | "final"; - - -.. ts:def:: TransactionStatePattern - - interface TransactionStatePattern { - major: TransactionMajorState | TransactionStateWildcard; - - minor?: TransactionMinorState | TransactionStateWildcard; - - // Required value of the "working" flag of the transaction - // state. A transaction state without the flag counts as - // false. If left undefined, the flag is not taken into - // account when matching, i.e. it behaves like a wildcard. - working?: boolean | TransactionStateWildcard; - } - - -.. ts:def:: TransactionStateWildcard - - type TransactionStateWildcard = "*"; - - -.. ts:def:: DurationUnitSpec - - // A duration given as a sum of the specified units; used for - // timeouts. - interface DurationUnitSpec { - seconds?: number; - - minutes?: number; - - hours?: number; - - days?: number; - - months?: number; - - years?: number; - } diff --git a/core/wallet-core/transactions/abort-transaction.rst b/core/wallet-core/transactions/abort-transaction.rst @@ -0,0 +1,38 @@ +.. ts:op:: abortTransaction + + Abort a transaction. + + **Request:** + + The request ``args`` must be an `AbortTransactionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Transitions the transaction into an aborting state and starts + abort processing, which may issue refunds or recover coins + (network activity). + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. + + **Details:** + + For payment transactions, aborting puts the payment into an + ``aborting`` state, in which the wallet recovers the spent coins + via a refund where possible before the transaction reaches a + final state. Whether a transaction can currently be aborted is + advertised in its ``txActions``. + +.. ts:def:: AbortTransactionRequest + + interface AbortTransactionRequest { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/transactions/delete-transaction.rst b/core/wallet-core/transactions/delete-transaction.rst @@ -0,0 +1,34 @@ +.. ts:op:: deleteTransaction + + Permanently delete a transaction from the wallet's transaction + history. + + **Request:** + + The request ``args`` must be a `DeleteTransactionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Deletes the transaction from the wallet's history. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. + + **Details:** + + Deleting is only possible in states that advertise the ``delete`` + action (in ``txActions``), typically final or dialog states. Any + background task associated with the transaction is stopped. + +.. ts:def:: DeleteTransactionRequest + + interface DeleteTransactionRequest { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/transactions/fail-transaction.rst b/core/wallet-core/transactions/fail-transaction.rst @@ -0,0 +1,39 @@ +.. ts:op:: failTransaction + + Mark a transaction as failed, giving up on its ongoing processing. + + **Request:** + + The request ``args`` must be a `FailTransactionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Marks an aborting transaction as finally failed, giving up on + the ongoing recovery. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. + + **Details:** + + This is typically used for transactions in an ``aborting`` state + (for example while recovering an aborted payment via a refund): + the user stops waiting for the recovery, and the transaction + transitions to the final ``failed`` state. The reason is + recorded in the transaction's ``failReason``. Whether a + transaction can currently be failed is advertised in its + ``txActions``. + +.. ts:def:: FailTransactionRequest + + interface FailTransactionRequest { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/transactions/get-transaction-by-id.rst b/core/wallet-core/transactions/get-transaction-by-id.rst @@ -0,0 +1,741 @@ +.. ts:op:: getTransactionById + :read-only: + + Get a single transaction by its identifier. + + **Request:** + + The request ``args`` must be a `TransactionByIdRequest` object. + + **Response:** + + On success, the result is a `Transaction` object. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + The ``transactionId`` must have the form of a `TransactionIdStr`; + malformed identifiers fail with ``WALLET_CORE_API_BAD_REQUEST``, + well-formed but unknown identifiers with + ``WALLET_TRANSACTION_NOT_FOUND``. + + The full contract terms (``contractTerms``) are only reported for + payment transactions and only when ``includeContractTerms`` is + set. + +.. ts:def:: Transaction + + // A transaction in the wallet's transaction history. The + // ``type`` field discriminates the union; all members share the + // fields of `TransactionCommon`. + type Transaction = + | TransactionWithdrawal + | TransactionPayment + | TransactionRefund + | TransactionRefresh + | TransactionDeposit + | TransactionPeerPullCredit + | TransactionPeerPullDebit + | TransactionPeerPushCredit + | TransactionPeerPushDebit + | TransactionInternalWithdrawal + | TransactionRecoup + | TransactionDenomLoss; + +.. ts:def:: TransactionIdStr + + // Opaque, stable identifier of a transaction, of the form + // ``txn:<type>:<id>`` where ``<type>`` is a `TransactionType`. + // (The TypeScript source additionally brands this string type; + // the brand only exists at compile time.) + type TransactionIdStr = `txn:${string}:${string}`; + +.. ts:def:: TransactionType + + type TransactionType = + | "withdrawal" + | "internal-withdrawal" + | "payment" + | "refund" + | "refresh" + | "deposit" + | "peer-push-debit" + | "peer-push-credit" + | "peer-pull-debit" + | "peer-pull-credit" + | "recoup" + | "denom-loss"; + +.. ts:def:: TransactionCommon + + interface TransactionCommon { + // Opaque unique ID for the transaction, used as a starting + // point for paginating queries and for invoking actions on the + // transaction (e.g. deleting it from the history). + transactionId: TransactionIdStr; + + // The transaction produced funds under an exchange key set that + // the user subsequently purged from the wallet. + legacy?: boolean; + + // Short identifier assigned by this wallet for local, + // human-facing use, of the form ``#<type>:<localIdent>``. + // Intentionally not portable: importing or merging a wallet can + // assign different local identifiers. Clients must use + // ``transactionId`` when they need a stable ID. Undefined when + // the wallet backend does not support local IDs. + localTransactionId?: string; + + // Type of the transaction; discriminates the `Transaction` + // union. + type: TransactionType; + + // Main timestamp of the transaction. + timestamp: TalerPreciseTimestamp; + + // Scopes of this transaction. + scopes: ScopeInfo[]; + + // Transaction state, as per DD37. + txState: TransactionState; + + // Wallet-internal state ID, only used for debugging and + // testing. + stId: number; + + // Possible transitions based on the current state. + txActions: TransactionAction[]; + + // Raw amount of the transaction (exclusive of fees or other + // extra costs). + amountRaw: AmountString; + + // Amount shown when the transaction was confirmed, including + // estimated fees. Preserved when execution fails, expires or + // is aborted. + amountEffective: AmountString; + + // Settled wallet balance effect, including fees and abort + // recovery. Nonnegative; the transaction type determines + // whether this is a debit or a credit. Absent until the + // transaction and its recovery have settled, or when the amount + // cannot be established for historical records. Ordinary + // merchant refunds remain separate credits. Associated + // refreshes have zero effect. + amountEffectiveFinal?: AmountString; + + error?: TalerErrorDetail; + + abortReason?: TalerErrorDetail; + + failReason?: TalerErrorDetail; + + // Location where the user must go to complete KYC; present + // when the transaction's minor state is ``kyc``. + kycUrl?: string; + + // KYC payto hash. Useful for testing, not so useful for UIs. + kycPaytoHash?: string; + + // KYC access token. Useful for testing, not so useful for UIs. + kycAccessToken?: string; + + kycAuthTransferInfo?: KycAuthTransferInfo; + } + +.. ts:def:: TransactionState + + interface TransactionState { + // Major state component of the transaction state. + major: TransactionMajorState; + + // Minor state component of the transaction. + minor?: TransactionMinorState; + + // Whether the wallet is currently actively processing the + // transaction or waiting for a counterparty. Will eventually + // be folded into a new major state. + working?: boolean; + } + +.. ts:def:: TransactionMajorState + + type TransactionMajorState = + // No state, only used when reporting transitions into the + // initial state. + | "none" + | "pending" + | "done" + | "aborting" + | "aborted" + | "dialog" + | "finalizing" + // A suspended pending state. + | "suspended" + | "suspended-finalizing" + | "suspended-aborting" + | "failed" + | "expired" + // Only used for notifications, never in the transaction + // history. + | "deleted"; + +.. ts:def:: TransactionMinorState + + type TransactionMinorState = + | "aborting-bank" + | "accept-refund" + | "auto-refund" + | "balance-kyc" + | "bank" + | "bank-confirm-transfer" + | "bank-register-reserve" + | "check-refund" + | "claim-proposal" + | "completed-by-other-wallet" + | "continued-with-other-wallet" + | "create-purse" + | "delete-purse" + | "deposit" + | "deposit-abort-partial" + | "deposit-abort-recovered" + | "deposit-abort-recovery-failed" + | "deposit-abort-refund-failed" + | "deposit-abort-too-late" + | "exchange" + | "exchange-wait-reserve" + | "kyc-auth" + | "kyc-hard-limit" + | "kyc-init" + | "kyc" + | "merge" + | "paid-by-other" + | "proposed" + | "ready" + | "rebind-session" + | "refresh" + | "refused" + | "repurchase" + | "submit-payment" + | "track" + | "unknown" + | "withdraw" + | "waiting-for-other-wallet" + | "abort"; + +.. ts:def:: TransactionAction + + // Actions the user can request on a transaction in its current + // state; each action corresponds to one of the transaction + // operations. + type TransactionAction = + | "delete" + | "suspend" + | "resume" + | "abort" + | "fail" + | "retry"; + +.. ts:def:: KycAuthTransferInfo + + interface KycAuthTransferInfo { + // Payto URI of the account that must make the transfer. The + // KYC auth transfer will *not* work if it originates from a + // different account. + debitPaytoUri: string; + + // Account public key. Included in the transfer subject for + // some of the transfer options. + accountPub: string; + + // Options for making the KYC auth transfer, grouped by exchange + // credit account in the same format used for withdrawals. + transferOptionsExt: WithdrawalExchangeAccountDetails[]; + + // Options for making the KYC auth transfer payment to the + // exchange. Deprecated: use ``transferOptionsExt`` instead. + transferOptions: TransferOption[]; + + // Validity of the transferOptions, or undefined if they do not + // expire. Deprecated: use the per-account expiry in + // ``transferOptionsExt`` instead. + transferExpiry: TalerProtocolTimestamp | undefined; + + // Amount that the exchange expects to be deposited. Usually + // the smallest amount that can be transferred via a bank + // transfer. Deprecated: use ``transferOptions`` instead. + amount: AmountString; + + // Possible target payto URIs. Deprecated: use + // ``transferOptions`` instead. + creditPaytoUris: string[]; + } + +.. ts:def:: TransactionWithdrawal + + // A withdrawal transaction (either bank-integrated or manual). + interface TransactionWithdrawal extends TransactionCommon { + type: "withdrawal"; + + // Exchange of the withdrawal. + exchangeBaseUrl: string | undefined; + + // Amount that got subtracted from the reserve balance. + amountRaw: AmountString; + + // Amount that actually was (or will be) added to the wallet's + // balance. + amountEffective: AmountString; + + withdrawalDetails: WithdrawalDetails; + } + +.. ts:def:: TransactionInternalWithdrawal + + // Internal withdrawal operation, only reported on request. Some + // transactions (peer-*-credit) internally do a withdrawal, but + // only the peer-*-credit transaction is reported. The internal + // withdrawal transaction gives access to the details of the + // underlying withdrawal for testing/debugging. It is usually not + // reported, so that the amounts of transactions properly add up. + interface TransactionInternalWithdrawal extends TransactionCommon { + type: "internal-withdrawal"; + + // Exchange of the withdrawal. + exchangeBaseUrl: string; + + // Amount that got subtracted from the reserve balance. + amountRaw: AmountString; + + // Amount that actually was (or will be) added to the wallet's + // balance. + amountEffective: AmountString; + + withdrawalDetails: WithdrawalDetails; + } + +.. ts:def:: WithdrawalDetails + + type WithdrawalDetails = + | WithdrawalDetailsForManualTransfer + | WithdrawalDetailsForTalerBankIntegrationApi; + +.. ts:def:: WithdrawalDetailsForManualTransfer + + interface WithdrawalDetailsForManualTransfer { + type: "manual-transfer"; + + // Payto URIs that the exchange supports. Already contains the + // amount and message. Deprecated: in favor of + // ``exchangeCreditAccountDetails``. + exchangePaytoUris: string[]; + + exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[]; + + // Public key of the reserve. + reservePub: string; + + // Is the reserve ready for withdrawal? + reserveIsReady: boolean; + + // How long the exchange waits to transfer back funds from a + // reserve. + reserveClosingDelay: TalerProtocolDuration; + } + +.. ts:def:: WithdrawalDetailsForTalerBankIntegrationApi + + interface WithdrawalDetailsForTalerBankIntegrationApi { + type: "taler-bank-integration-api"; + + // True if the bank has confirmed the withdrawal. An + // unconfirmed withdrawal usually requires user input and should + // be highlighted in the UI; see ``bankConfirmationUrl``. + confirmed: boolean; + + // If the withdrawal is unconfirmed, this can include a URL for + // user-initiated confirmation. + bankConfirmationUrl?: string; + + // Public key of the reserve. + reservePub: string; + + // Is the reserve ready for withdrawal? + reserveIsReady: boolean; + + // Is the bank transfer for the withdrawal externally + // confirmed? + externalConfirmation?: boolean; + + exchangeCreditAccountDetails?: WithdrawalExchangeAccountDetails[]; + } + +.. ts:def:: TransactionPayment + + interface TransactionPayment extends TransactionCommon { + type: "payment"; + + // Merchant instance base URL used to claim the order. + // Available even before the contract terms have been + // downloaded. + merchantBaseUrl: string; + + // Public payment URI shown while this wallet waits for another + // wallet to claim an order that it released. + unclaimedPayUri?: TalerUriString; + + // Additional information about the payment. Only present if + // the information about the order is already available. + info: OrderShortInfo | undefined; + + // Full contract terms. Only included if explicitly requested + // via the ``includeContractTerms`` flag of + // ``getTransactionById``. + contractTerms?: MerchantContractTerms; + + // Amount that must be paid for the contract. + amountRaw: AmountString; + + // Amount that was paid, including deposit, wire and refresh + // fees. + amountEffective: AmountString; + + // Amount that has been refunded by the merchant. + totalRefundRaw: AmountString; + + // Amount that will be added to the wallet's balance after fees + // and refreshing. + totalRefundEffective: AmountString; + + // Amount pending to be picked up. + refundPending: AmountString | undefined; + + // Reference to applied refunds. + refunds: RefundInfoShort[]; + + // Is the wallet currently checking for a refund? + refundQueryActive: boolean; + + // PoS confirmation codes, separated by newlines. Only present + // for purchases that support PoS confirmation. + posConfirmation: string | undefined; + + // Until when the ``posConfirmation`` is valid. + posConfirmationDeadline?: TalerProtocolTimestamp; + + // Did we receive the payment via a taler://pay-template/ URI + // and did the URI contain a nfc=1 flag? + posConfirmationViaNfc?: boolean; + + // In case this payment transaction was detected as a + // repurchase, the transaction ID of the original payment. + repurchaseTransactionId?: TransactionIdStr; + + // If applicable, the choice that the user selected. + choiceIndex?: number; + } + +.. ts:def:: OrderShortInfo + + interface OrderShortInfo { + // Order ID, uniquely identifies the order within a merchant + // instance. + orderId: string; + + // Hash of the contract terms. + contractTermsHash: string; + + // More information about the merchant. + merchant: MerchantInfo; + + // Summary of the order, given by the merchant. + summary: string; + + // Map from IETF BCP 47 language tags to localized summaries. + summary_i18n?: InternationalizedString; + + // URL of the fulfillment, given by the merchant. + fulfillmentUrl?: string; + + // Plain text message that should be shown to the user when the + // payment is complete. + fulfillmentMessage?: string; + + // Translations of ``fulfillmentMessage``. + fulfillmentMessage_i18n?: InternationalizedString; + } + +.. ts:def:: RefundInfoShort + + interface RefundInfoShort { + transactionId: string; + + timestamp: TalerProtocolTimestamp; + + amountEffective: AmountString; + + amountRaw: AmountString; + } + +.. ts:def:: TransactionRefund + + interface TransactionRefund extends TransactionCommon { + // Recovery already included in the unsuccessful payment's + // final cost. + isAbortRecovery?: boolean; + + type: "refund"; + + // Amount that has been refunded by the merchant. + amountRaw: AmountString; + + // Amount that will be added to the wallet's balance after fees + // and refreshing. + amountEffective: AmountString; + + // ID of the transaction that is refunded. + refundedTransactionId: string; + + paymentInfo: RefundPaymentInfo | undefined; + } + +.. ts:def:: RefundPaymentInfo + + // Summary information about the payment that we got a refund + // for. + interface RefundPaymentInfo { + summary: string; + + summary_i18n?: InternationalizedString; + + // More information about the merchant. + merchant: MerchantInfo; + } + +.. ts:def:: TransactionRefresh + + // A transaction shown for refreshes. Only shown for (1) + // refreshes not associated with other transactions and (2) + // refreshes in an error state. + interface TransactionRefresh extends TransactionCommon { + type: "refresh"; + + refreshReason: RefreshReason; + + // Transaction ID that caused this refresh. + originatingTransactionId?: string; + + // Always zero for refreshes. + amountRaw: AmountString; + + // Fees, i.e. the effective, negative effect of the refresh on + // the balance. Only applicable for stand-alone refreshes, and + // zero for other refreshes where the transaction itself + // accounts for the refresh fee. + amountEffective: AmountString; + + refreshInputAmount: AmountString; + + refreshOutputAmount: AmountString; + } + +.. ts:def:: RefreshReason + + // Reason why a coin is being refreshed. + type RefreshReason = + | "manual" + | "pay-merchant" + | "pay-deposit" + | "pay-peer-push" + | "pay-peer-pull" + | "refund" + | "abort-pay" + | "abort-deposit" + | "abort-peer-push-debit" + | "abort-peer-pull-debit" + | "recoup" + | "backup-restored" + | "scheduled"; + +.. ts:def:: TransactionDeposit + + // Deposit transaction, which effectively sends money from this + // wallet somewhere else. + interface TransactionDeposit extends TransactionCommon { + type: "deposit"; + + depositGroupId: string; + + // Target for the deposit. + targetPaytoUri: string; + + // Raw amount that is being deposited. + amountRaw: AmountString; + + // Deposit account public key. + accountPub: string; + + // Effective amount that is being deposited. + amountEffective: AmountString; + + wireTransferDeadline: TalerProtocolTimestamp; + + wireTransferProgress: number; + + // Did all the deposit requests succeed? + deposited: boolean; + + trackingState: Array<DepositTransactionTrackingState>; + } + +.. ts:def:: DepositTransactionTrackingState + + interface DepositTransactionTrackingState { + // Raw wire transfer identifier of the deposit. + wireTransferId: string; + + // When the wire transfer was given to the bank. + timestampExecuted: TalerProtocolTimestamp; + + // Total amount transferred for this wtid (including fees). + amountRaw: AmountString; + + // Wire fee amount for this exchange. + wireFee: AmountString; + } + +.. ts:def:: TransactionPeerPullCredit + + // Credit because we were paid for a P2P invoice we created. + interface TransactionPeerPullCredit extends TransactionCommon { + type: "peer-pull-credit"; + + info: PeerInfoShort; + + // Exchange used. + exchangeBaseUrl: string; + + // Amount that got subtracted from the reserve balance. + amountRaw: AmountString; + + // Amount that actually was (or will be) added to the wallet's + // balance. + amountEffective: AmountString; + + // URI to send to the other party. Only available in the right + // state. + talerUri: string | undefined; + } + +.. ts:def:: TransactionPeerPullDebit + + // Debit because we paid someone's invoice. + interface TransactionPeerPullDebit extends TransactionCommon { + type: "peer-pull-debit"; + + info: PeerInfoShort; + + // Exchange used. + exchangeBaseUrl: string; + + amountRaw: AmountString; + + amountEffective: AmountString; + } + +.. ts:def:: TransactionPeerPushDebit + + // We sent money via a P2P payment. + interface TransactionPeerPushDebit extends TransactionCommon { + type: "peer-push-debit"; + + info: PeerInfoShort; + + // Exchange used. + exchangeBaseUrl: string; + + // Amount that got subtracted from the reserve balance. + amountRaw: AmountString; + + // Amount that actually was (or will be) added to the wallet's + // balance. + amountEffective: AmountString; + + // URI to accept the payment. Only present if the transaction + // is in a state where the other party can accept the payment. + talerUri?: string; + } + +.. ts:def:: TransactionPeerPushCredit + + // We received money via a P2P payment. + interface TransactionPeerPushCredit extends TransactionCommon { + type: "peer-push-credit"; + + info: PeerInfoShort; + + // Exchange used. + exchangeBaseUrl: string; + + // Amount that got subtracted from the reserve balance. + amountRaw: AmountString; + + // Amount that actually was (or will be) added to the wallet's + // balance. + amountEffective: AmountString; + } + +.. ts:def:: PeerInfoShort + + interface PeerInfoShort { + expiration: TalerProtocolTimestamp | undefined; + + summary: string | undefined; + + iconId: string | undefined; + } + +.. ts:def:: TransactionRecoup + + // The exchange revoked a key and the wallet recoups funds. + interface TransactionRecoup extends TransactionCommon { + type: "recoup"; + } + +.. ts:def:: TransactionDenomLoss + + // A transaction to indicate financial loss due to denominations + // that became unusable for deposits. + interface TransactionDenomLoss extends TransactionCommon { + type: "denom-loss"; + + lossEventType: DenomLossEventType; + + exchangeBaseUrl: string; + } + +.. ts:def:: DenomLossEventType + + type DenomLossEventType = + | "denom-expired" + | "denom-vanished" + | "denom-unoffered" + // The exchange revoked the denomination. A revoked + // denomination also stops being offered, so this must be + // checked before "denom-unoffered" to say what actually + // happened. + | "denom-revoked"; + +.. ts:def:: TransactionByIdRequest + + interface TransactionByIdRequest { + transactionId: string; + + // If set to true, report the full contract terms in the + // response if the transaction has them. + includeContractTerms?: boolean; + } diff --git a/core/wallet-core/transactions/get-transactions-v2.rst b/core/wallet-core/transactions/get-transactions-v2.rst @@ -0,0 +1,85 @@ +.. ts:op:: getTransactionsV2 + :read-only: + + Get the wallet's transaction list, with support for paginated + queries. + + **Request:** + + The request ``args`` must be a `GetTransactionsV2Request` object. + + **Response:** + + On success, the result is a `TransactionsResponse` object. + + **Details:** + + Without a ``limit`` and without an offset, all matching + transactions are returned in ascending timestamp order. + + With a positive ``limit``, at most ``limit`` transactions are + returned in ascending order: without an offset starting at the + first transaction, with an offset starting after the offset. With + a negative ``limit``, at most ``-limit`` transactions are returned + in descending order: without an offset starting at the last + transaction, with an offset starting before the offset. + Transactions with equal timestamps are ordered by their + ``transactionId``. + + If the ``offsetTransactionId`` no longer exists (for example + because the transaction was deleted), ``offsetTimestamp`` is used + as a fallback anchor. If the offset transaction does not exist + and no ``offsetTimestamp`` is given, the request fails with + ``WALLET_TRANSACTION_NOT_FOUND``. + + Refresh transactions are excluded unless ``includeRefreshes`` (or + ``includeAll``) is set; payments that were superseded by a + repurchase are excluded unless ``includeAll`` is set. + +.. ts:def:: GetTransactionsV2Request + + interface GetTransactionsV2Request { + // Return only transactions in the given currency. + currency?: string; + + // Return only transactions in the given scope. + scopeInfo?: ScopeInfo; + + // If true, include all refreshes in the transaction list. + includeRefreshes?: boolean; + + // If true, include transactions that would usually be filtered + // out. Implies ``includeRefreshes``. + includeAll?: boolean; + + // Only return transactions before/after this offset. + offsetTransactionId?: TransactionIdStr; + + // Only return transactions before/after the transaction with + // this timestamp. Used as a fallback if the + // ``offsetTransactionId`` was deleted. + offsetTimestamp?: TalerPreciseTimestamp; + + // Number of transactions to return. When positive, results are + // returned in ascending timestamp order (starting at the first + // transaction or after the offset). When negative, results + // are returned in descending timestamp order (starting at the + // last transaction or before the offset). + limit?: number; + + // Filter transactions by their state / state category. + // If not specified, all transactions are returned. + // "final": transactions in any final state; + // "nonfinal": transactions in any state but the final states; + // "nonfinal-dialog": nonfinal transactions that require + // confirmation / some choice by the user; + // "nonfinal-approved": nonfinal transactions that need no + // further user approval; + // "done": transactions in the "done" major state. + filterByState?: + | "final" + | "nonfinal" + | "done" + | "nonfinal-approved" + | "nonfinal-dialog"; + } diff --git a/core/wallet-core/transactions/get-transactions.rst b/core/wallet-core/transactions/get-transactions.rst @@ -0,0 +1,76 @@ +.. ts:op:: getTransactions + :read-only: + + Get the wallet's transaction list, containing past and pending + transactions. + + **Request:** + + The request ``args`` must be a `TransactionsRequest` object. + + **Response:** + + On success, the result is a `TransactionsResponse` object. + + **Details:** + + Refresh transactions are excluded from the result unless + ``includeRefreshes`` is set. + + With the default sort orders (``ascending`` and ``descending``), + pending transactions (major states ``pending``, ``aborting`` and + ``dialog``) are listed before all other transactions; within each + group, transactions are sorted by their timestamp, and ties are + broken by a fixed transaction-type order. With + ``stable-ascending``, all transactions are sorted purely by + ascending timestamp, so pending transactions do not jump around. + + Filtering by an auditor scope (``scopeInfo`` of type ``auditor``) + is not supported and fails with ``WALLET_CORE_API_BAD_REQUEST``. + +.. ts:def:: TransactionsRequest + + interface TransactionsRequest { + // Return only transactions in the given currency. + // Deprecated: use ``scopeInfo`` instead. + currency?: string; + + // Return only transactions in the given scope. + scopeInfo?: ScopeInfo; + + // Limit results to transactions related to the given search + // string. Currently accepted but not applied by wallet-core. + search?: string; + + // Sort order of the transaction items. "ascending" (the + // default) and "descending" sort by timestamp but list pending + // transactions first; "stable-ascending" sorts purely by + // ascending timestamp, with pending transactions in between. + sort?: "ascending" | "descending" | "stable-ascending"; + + // If true, include all refreshes in the transaction list. + includeRefreshes?: boolean; + + // If set, only return transactions matching the state filter. + filterByState?: TransactionStateFilter; + } + +.. ts:def:: TransactionStateFilter + + // State filter for the transaction list: only transactions in a + // non-final state. + type TransactionStateFilter = "nonfinal"; + +.. ts:def:: TransactionsResponse + + interface TransactionsResponse { + // List of past and pending transactions matching the request; + // see the respective operation for the sort order. + transactions: Transaction[]; + } + +.. ts:def:: WithdrawalType + + type WithdrawalType = + | "taler-bank-integration-api" + | "manual-transfer"; diff --git a/core/wallet-core/transactions/list-associated-refreshes.rst b/core/wallet-core/transactions/list-associated-refreshes.rst @@ -0,0 +1,28 @@ +.. ts:op:: listAssociatedRefreshes + + List the refresh transactions associated with another transaction. + + This operation is declared but not implemented yet: it currently + always fails with ``GENERIC_FEATURE_NOT_IMPLEMENTED``. + + **Request:** + + The request ``args`` must be a `ListAssociatedRefreshesRequest` + object. + + **Response:** + + On success, the result is a `ListAssociatedRefreshesResponse` + object. + +.. ts:def:: ListAssociatedRefreshesRequest + + interface ListAssociatedRefreshesRequest { + transactionId: string; + } + +.. ts:def:: ListAssociatedRefreshesResponse + + interface ListAssociatedRefreshesResponse { + transactionIds: string[]; + } diff --git a/core/wallet-core/transactions/resolve-transaction-reference.rst b/core/wallet-core/transactions/resolve-transaction-reference.rst @@ -0,0 +1,48 @@ +.. ts:op:: resolveTransactionReference + :read-only: + + Resolve a stable transaction ID, a wallet-local identifier, or an + external withdrawal reference to a stable transaction ID. + + **Request:** + + The request ``args`` must be a `ResolveTransactionReferenceRequest` + object. + + **Response:** + + On success, the result is a `ResolveTransactionReferenceResponse` + object. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, ``WALLET_CORE_API_BAD_REQUEST``. + + **Details:** + + Three kinds of references are accepted: + + * a stable transaction ID (`TransactionIdStr`), which is returned + unchanged; + * a wallet-local identifier of the form ``#<type>:<localIdent>``, + as reported in the transaction's ``localTransactionId`` field. + Local identifiers are deliberately not portable: importing or + merging a wallet can assign different local identifiers; + * an external, bank-generated withdrawal reference (such as an + LSD 0006 withdrawal-transfer-result reference) that contains + the reserve public key of a withdrawal. The reference must + match exactly one withdrawal transaction, otherwise the request + fails with ``WALLET_TRANSACTION_NOT_FOUND``. + +.. ts:def:: ResolveTransactionReferenceRequest + + interface ResolveTransactionReferenceRequest { + transactionReference: string; + } + +.. ts:def:: ResolveTransactionReferenceResponse + + interface ResolveTransactionReferenceResponse { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/transactions/resume-transaction.rst b/core/wallet-core/transactions/resume-transaction.rst @@ -0,0 +1,24 @@ +.. ts:op:: resumeTransaction + + Resume a transaction that was previously suspended with + :ts:op:`suspendTransaction`. + + **Request:** + + The request ``args`` must be an `AbortTransactionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Resumes a suspended transaction; its processing restarts, + possibly with network activity. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. diff --git a/core/wallet-core/transactions/retry-transaction.rst b/core/wallet-core/transactions/retry-transaction.rst @@ -0,0 +1,34 @@ +.. ts:op:: retryTransaction + + Immediately retry the transaction's underlying operation by + resetting the retry timer of its background task. + + **Request:** + + The request ``args`` must be a `RetryTransactionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Immediately retries processing of the transaction; network + activity is possible. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``. + + **Details:** + + Retrying is only possible in states that advertise the ``retry`` + action (in ``txActions``); the request does not wait for the + retried operation to complete. + +.. ts:def:: RetryTransactionRequest + + interface RetryTransactionRequest { + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/transactions/suspend-transaction.rst b/core/wallet-core/transactions/suspend-transaction.rst @@ -0,0 +1,25 @@ +.. ts:op:: suspendTransaction + + Suspend a transaction, stopping any associated network activities + while keeping the option of trying again at a later time. This can + be useful to save battery power or bandwidth when an operation is + expected to take longer, such as a very large withdrawal. + + **Request:** + + The request ``args`` must be an `AbortTransactionRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Suspends a pending transaction; its processing stops. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TRANSACTION_NOT_FOUND``, + ``WALLET_TRANSACTION_ACTION_UNSUPPORTED``, + ``WALLET_REQUEST_TRANSACTION_STATE_UNSUPPORTED``. diff --git a/core/wallet-core/transactions/wait-transaction-state.rst b/core/wallet-core/transactions/wait-transaction-state.rst @@ -0,0 +1,154 @@ +.. ts:op:: waitTransactionState + :read-only: + + Wait until a transaction is in a particular state. The operation + blocks until the transaction reaches the requested state, an + error occurs, or the timeout expires. + + **Request:** + + The request ``args`` must be a `WaitTransactionStateRequest` + object. + + **Response:** + + On success, the result is a `WaitTransactionStateResponse` + object. + + **Details:** + + This is a long-polling operation: it returns once the + transaction's state matches ``txState``, matches one of the + ``bailStates``, or (with ``bailOnError``) an error is recorded + for the transaction. The response's ``matched`` field says which + of the two sets of states ended the wait. + + The ``txState`` and ``bailStates`` fields are + `TestingWaitTxStateSpec` values: a `TransactionStatePattern` or a + list of patterns (matching when any pattern matches), a + wallet-internal numeric state ID, or one of the shorthands + ``nonpending`` (any major state other than ``pending``) and + ``final`` (any final major state). In a pattern, ``major``, + ``minor`` and ``working`` accept the wildcard ``*``; a pattern + without ``minor`` only matches states that have no minor state, + while a pattern without ``working`` matches regardless of the + flag. + + Without ``bailStates`` or ``bailOnError``, a transaction that + reaches a state it will never leave keeps the caller waiting + until the ``timeout`` expires; the wait then fails with + ``GENERIC_TIMEOUT``. + + When ``progressToken`` is set, the wait reports request + progress notifications (including recorded transaction errors + with their retry counter and remaining retry delay) and can be + cancelled with :ts:op:`cancelProgressToken` or nudged with + :ts:op:`retryProgressTokenNow`. Cancellation stops only the + wait, not the transaction. + +.. ts:def:: WaitTransactionStateRequest + + interface WaitTransactionStateRequest { + transactionId: TransactionIdStr; + + // Receive request progress notifications and control this wait + // via ``cancelProgressToken``/``retryProgressTokenNow``. + // Cancellation stops only the wait. Retry-now retries the + // transaction if its current state allows it, without + // restarting the wait or extending its timeout. Transaction + // errors are reported with their recorded retry counter and + // remaining delay; without a recorded error retry, the counter + // is zero and the delay is "forever". + progressToken?: string; + + // Additional identifier that is used in the logs to easily + // find the status of the particular wait request. + logId?: string; + + // After the timeout has passed, give up on waiting for the + // desired state and raise an error instead. + timeout?: DurationUnitSpec; + + // If set to true, wait until the desired state is reached + // with an error. + requireError?: boolean; + + // State(s) to wait for. + txState: TestingWaitTxStateSpec; + + // States that end the wait even though they are not the state + // that was waited for. The response says which of the two + // sets matched. Without this, a transaction that ends up in + // a state it will never leave keeps the caller waiting until + // the timeout. + bailStates?: TestingWaitTxStateSpec; + + // End the wait as soon as an error is recorded for the + // transaction. Beware that this includes transient errors of + // retried operations, which are cleared again once the + // operation succeeds. + bailOnError?: boolean; + } + +.. ts:def:: WaitTransactionStateResponse + + interface WaitTransactionStateResponse { + // Which set of states ended the wait: the requested state or + // one of the bail states. + matched: "target" | "bail"; + + // State that ended the wait. + txState: TransactionState; + + // Wallet-internal state ID, only used for debugging and + // testing. + stId: number; + } + +.. ts:def:: TestingWaitTxStateSpec + + // State(s) to wait for: a plain pattern or a list of patterns + // (matching any of them), a wallet-internal numeric state ID, or + // one of the shorthands for a category of states. + type TestingWaitTxStateSpec = + | TransactionStatePattern + | TransactionStatePattern[] + | number + | "nonpending" + | "final"; + +.. ts:def:: TransactionStatePattern + + interface TransactionStatePattern { + major: TransactionMajorState | TransactionStateWildcard; + + minor?: TransactionMinorState | TransactionStateWildcard; + + // Required value of the "working" flag of the transaction + // state. A transaction state without the flag counts as + // false. If left undefined, the flag is not taken into + // account when matching, i.e. it behaves like a wildcard. + working?: boolean | TransactionStateWildcard; + } + +.. ts:def:: TransactionStateWildcard + + type TransactionStateWildcard = "*"; + +.. ts:def:: DurationUnitSpec + + // A duration given as a sum of the specified units; used for + // timeouts. + interface DurationUnitSpec { + seconds?: number; + + minutes?: number; + + hours?: number; + + days?: number; + + months?: number; + + years?: number; + } diff --git a/core/wallet-core/validation.rst b/core/wallet-core/validation.rst @@ -1,230 +0,0 @@ -.. _wallet-op-validateIban: - -**validateIban** - -Validate an International Bank Account Number (IBAN) according to -ISO 13616, including the country-specific length and the checksum. - -**Request:** - - The request must be a `ValidateIbanRequest` object. - -**Response:** - - On success, the result is a `ValidateIbanResponse` object. - -.. ts:def:: ValidateIbanRequest - - interface ValidateIbanRequest { - iban: string; - } - -.. ts:def:: ValidateIbanResponse - - interface ValidateIbanResponse { - valid: boolean; - } - - -.. _wallet-op-canonicalizeBaseUrl: - -**canonicalizeBaseUrl** - -Canonicalize a base URL the way wallet-core does for exchange and -auditor base URLs. - -**Request:** - - The request must be a `CanonicalizeBaseUrlRequest` object. - -**Response:** - - On success, the result is a `CanonicalizeBaseUrlResponse` object. - -**Details:** - - An ``https://`` scheme is prepended when the URL has no scheme, a - trailing slash is appended to the path when missing, and any query - and fragment are removed. - -.. ts:def:: CanonicalizeBaseUrlRequest - - interface CanonicalizeBaseUrlRequest { - url: string; - } - -.. ts:def:: CanonicalizeBaseUrlResponse - - interface CanonicalizeBaseUrlResponse { - url: string; - } - - -.. _wallet-op-convertIbanAccountFieldToPayto: - -**convertIbanAccountFieldToPayto** - -Convert user input for a bank account number into an IBAN payto -URI. - -**Request:** - - The request must be a `ConvertIbanAccountFieldToPaytoRequest` - object. - -**Response:** - - On success, the result is a - `ConvertIbanAccountFieldToPaytoResponse` object. - -**Details:** - - Dashes and spaces are stripped from the input. For ``HUF``, the - input is treated as a Hungarian BBAN and converted to an IBAN (a - developer experiment enables the same conversion for Swiss - BBANs); for other currencies, the input must be a valid IBAN. - When the input cannot be converted, ``ok`` is ``false``. - -.. ts:def:: ConvertIbanAccountFieldToPaytoRequest - - interface ConvertIbanAccountFieldToPaytoRequest { - value: string; - currency: string; - } - -.. ts:def:: ConvertIbanAccountFieldToPaytoResponse - - type ConvertIbanAccountFieldToPaytoResponse = - | { ok: true; type: "iban" | "bban"; paytoUri: string } - | { ok: false }; - - -.. _wallet-op-convertIbanPaytoToAccountField: - -**convertIbanPaytoToAccountField** - -Convert an IBAN payto URI into the account number representation -expected by the target country's banking interfaces. - -**Request:** - - The request must be a `ConvertIbanPaytoToAccountFieldRequest` - object. - -**Response:** - - On success, the result is a - `ConvertIbanPaytoToAccountFieldResponse` object. - -**Details:** - - Hungarian IBANs are converted to the domestic BBAN form (a - developer experiment enables the same conversion for Swiss - IBANs); all other IBANs are returned unchanged. - -.. ts:def:: ConvertIbanPaytoToAccountFieldRequest - - interface ConvertIbanPaytoToAccountFieldRequest { - paytoUri: string; - } - -.. ts:def:: ConvertIbanPaytoToAccountFieldResponse - - interface ConvertIbanPaytoToAccountFieldResponse { - type: "iban" | "bban"; - value: string; - } - - -.. _wallet-op-getBankingChoicesForPayto: - -**getBankingChoicesForPayto** - -Get banking applications or websites that can execute the given -payto URI. - -**Request:** - - The request must be a `GetBankingChoicesForPaytoRequest` object. - -**Response:** - - On success, the result is a `GetBankingChoicesForPaytoResponse` - object. - -**Details:** - - Currently, choices are only returned for the ``KUDOS`` - demonstration currency (links to the demonstrator bank's website - and app); the list is empty when the payto URI carries no amount - or no choice is known for the currency. - -.. ts:def:: GetBankingChoicesForPaytoRequest - - interface GetBankingChoicesForPaytoRequest { - paytoUri: string; - } - -.. ts:def:: GetBankingChoicesForPaytoResponse - - interface GetBankingChoicesForPaytoResponse { - choices: BankingChoiceSpec[]; - } - -.. ts:def:: BankingChoiceSpec - - interface BankingChoiceSpec { - label: string; - type: "link"; - uri: string; - } - - -.. _wallet-op-getQrCodesForPayto: - -**getQrCodesForPayto** - -Get QR code representations of a payto URI, for example to let the -user scan the code with a banking app. - -**Request:** - - The request must be a `GetQrCodesForPaytoRequest` object. - -**Response:** - - On success, the result is a `GetQrCodesForPaytoResponse` object. - -**Details:** - - All applicable QR code specifications are returned (an EPC QR - code and/or a Swiss QR bill); the list is empty when none - applies to the given payto URI. - -.. ts:def:: GetQrCodesForPaytoRequest - - interface GetQrCodesForPaytoRequest { - paytoUri: string; - } - -.. ts:def:: GetQrCodesForPaytoResponse - - interface GetQrCodesForPaytoResponse { - codes: QrCodeSpec[]; - } - -.. ts:def:: QrCodeSpec - - // Specification of a QR code that includes payment information. - interface QrCodeSpec { - // Type of the QR code. Depending on the type, different - // visual styles might be applied. - type: SupportedBankQr; - - // Content of the QR code that should be rendered. - qrContent: string; - } - -.. ts:def:: SupportedBankQr - - type SupportedBankQr = "epc-qr" | "spc"; diff --git a/core/wallet-core/validation/canonicalize-base-url.rst b/core/wallet-core/validation/canonicalize-base-url.rst @@ -0,0 +1,32 @@ +.. ts:op:: canonicalizeBaseUrl + :read-only: + + Canonicalize a base URL the way wallet-core does for exchange and + auditor base URLs. + + **Request:** + + The request must be a `CanonicalizeBaseUrlRequest` object. + + **Response:** + + On success, the result is a `CanonicalizeBaseUrlResponse` + object. + + **Details:** + + An ``https://`` scheme is prepended when the URL has no scheme, + a trailing slash is appended to the path when missing, and any + query and fragment are removed. + +.. ts:def:: CanonicalizeBaseUrlRequest + + interface CanonicalizeBaseUrlRequest { + url: string; + } + +.. ts:def:: CanonicalizeBaseUrlResponse + + interface CanonicalizeBaseUrlResponse { + url: string; + } diff --git a/core/wallet-core/validation/convert-iban-account-field-to-payto.rst b/core/wallet-core/validation/convert-iban-account-field-to-payto.rst @@ -0,0 +1,36 @@ +.. ts:op:: convertIbanAccountFieldToPayto + :read-only: + + Convert user input for a bank account number into an IBAN payto + URI. + + **Request:** + + The request must be a `ConvertIbanAccountFieldToPaytoRequest` + object. + + **Response:** + + On success, the result is a + `ConvertIbanAccountFieldToPaytoResponse` object. + + **Details:** + + Dashes and spaces are stripped from the input. For ``HUF``, + the input is treated as a Hungarian BBAN and converted to an + IBAN (a developer experiment enables the same conversion for + Swiss BBANs); for other currencies, the input must be a valid + IBAN. When the input cannot be converted, ``ok`` is ``false``. + +.. ts:def:: ConvertIbanAccountFieldToPaytoRequest + + interface ConvertIbanAccountFieldToPaytoRequest { + value: string; + currency: string; + } + +.. ts:def:: ConvertIbanAccountFieldToPaytoResponse + + type ConvertIbanAccountFieldToPaytoResponse = + | { ok: true; type: "iban" | "bban"; paytoUri: string } + | { ok: false }; diff --git a/core/wallet-core/validation/convert-iban-payto-to-account-field.rst b/core/wallet-core/validation/convert-iban-payto-to-account-field.rst @@ -0,0 +1,34 @@ +.. ts:op:: convertIbanPaytoToAccountField + :read-only: + + Convert an IBAN payto URI into the account number representation + expected by the target country's banking interfaces. + + **Request:** + + The request must be a `ConvertIbanPaytoToAccountFieldRequest` + object. + + **Response:** + + On success, the result is a + `ConvertIbanPaytoToAccountFieldResponse` object. + + **Details:** + + Hungarian IBANs are converted to the domestic BBAN form (a + developer experiment enables the same conversion for Swiss + IBANs); all other IBANs are returned unchanged. + +.. ts:def:: ConvertIbanPaytoToAccountFieldRequest + + interface ConvertIbanPaytoToAccountFieldRequest { + paytoUri: string; + } + +.. ts:def:: ConvertIbanPaytoToAccountFieldResponse + + interface ConvertIbanPaytoToAccountFieldResponse { + type: "iban" | "bban"; + value: string; + } diff --git a/core/wallet-core/validation/get-banking-choices-for-payto.rst b/core/wallet-core/validation/get-banking-choices-for-payto.rst @@ -0,0 +1,42 @@ +.. ts:op:: getBankingChoicesForPayto + :read-only: + + Get banking applications or websites that can execute the given + payto URI. + + **Request:** + + The request must be a `GetBankingChoicesForPaytoRequest` + object. + + **Response:** + + On success, the result is a `GetBankingChoicesForPaytoResponse` + object. + + **Details:** + + Currently, choices are only returned for the ``KUDOS`` + demonstration currency (links to the demonstrator bank's + website and app); the list is empty when the payto URI carries + no amount or no choice is known for the currency. + +.. ts:def:: GetBankingChoicesForPaytoRequest + + interface GetBankingChoicesForPaytoRequest { + paytoUri: string; + } + +.. ts:def:: GetBankingChoicesForPaytoResponse + + interface GetBankingChoicesForPaytoResponse { + choices: BankingChoiceSpec[]; + } + +.. ts:def:: BankingChoiceSpec + + interface BankingChoiceSpec { + label: string; + type: "link"; + uri: string; + } diff --git a/core/wallet-core/validation/get-qr-codes-for-payto.rst b/core/wallet-core/validation/get-qr-codes-for-payto.rst @@ -0,0 +1,52 @@ +.. ts:op:: getQrCodesForPayto + :read-only: + :deprecated: + + This operation is **deprecated**. Consult the withdrawal + transaction details instead. + + Get QR code representations of a payto URI, for example to let + the user scan the code with a banking app. + + **Request:** + + The request must be a `GetQrCodesForPaytoRequest` object. + + **Response:** + + On success, the result is a `GetQrCodesForPaytoResponse` + object. + + **Details:** + + All applicable QR code specifications are returned (an EPC QR + code and/or a Swiss QR bill); the list is empty when none + applies to the given payto URI. + +.. ts:def:: GetQrCodesForPaytoRequest + + interface GetQrCodesForPaytoRequest { + paytoUri: string; + } + +.. ts:def:: GetQrCodesForPaytoResponse + + interface GetQrCodesForPaytoResponse { + codes: QrCodeSpec[]; + } + +.. ts:def:: QrCodeSpec + + // Specification of a QR code that includes payment information. + interface QrCodeSpec { + // Type of the QR code. Depending on the type, different + // visual styles might be applied. + type: SupportedBankQr; + + // Content of the QR code that should be rendered. + qrContent: string; + } + +.. ts:def:: SupportedBankQr + + type SupportedBankQr = "epc-qr" | "spc"; diff --git a/core/wallet-core/validation/validate-iban.rst b/core/wallet-core/validation/validate-iban.rst @@ -0,0 +1,25 @@ +.. ts:op:: validateIban + :read-only: + + Validate an International Bank Account Number (IBAN) according to + ISO 13616, including the country-specific length and the checksum. + + **Request:** + + The request must be a `ValidateIbanRequest` object. + + **Response:** + + On success, the result is a `ValidateIbanResponse` object. + +.. ts:def:: ValidateIbanRequest + + interface ValidateIbanRequest { + iban: string; + } + +.. ts:def:: ValidateIbanResponse + + interface ValidateIbanResponse { + valid: boolean; + } diff --git a/core/wallet-core/withdrawals.rst b/core/wallet-core/withdrawals.rst @@ -1,625 +0,0 @@ -.. _wallet-op-prepareWithdrawExchange: - -**prepareWithdrawExchange** - -Prepare for withdrawing via a ``taler://withdraw-exchange`` URI. - -The URI names the exchange to withdraw from and may fix an amount. -Wallet-core fetches (or updates) the exchange entry, ephemerally adding -the exchange to the wallet's known exchanges if it is not known yet, -and checks that the currency of the URI's amount matches the exchange's -currency. - -This is the first step of a manual withdrawal that is initiated on the -exchange's side. The client can then query the fees and other details -with :ref:`getWithdrawalDetailsForAmount -<wallet-op-getWithdrawalDetailsForAmount>` and create the withdrawal -with :ref:`acceptManualWithdrawal <wallet-op-acceptManualWithdrawal>`. - -**Request:** - - The request must be a `PrepareWithdrawExchangeRequest` object. - -**Response:** - - On success, the result is a `PrepareWithdrawExchangeResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``, ``GENERIC_CURRENCY_MISMATCH``. - -.. ts:def:: PrepareWithdrawExchangeRequest - - interface PrepareWithdrawExchangeRequest { - // A taler://withdraw-exchange URI. - talerUri: string; - - progressToken?: string; - } - -.. ts:def:: PrepareWithdrawExchangeResponse - - interface PrepareWithdrawExchangeResponse { - // Base URL of the exchange that already existed or was - // ephemerally added as an exchange entry to the wallet. - exchangeBaseUrl: string; - - // Amount from the taler://withdraw-exchange URI. - // Only present if specified in the URI. - amount?: AmountString; - } - - -.. _wallet-op-prepareBankIntegratedWithdrawal: - -**prepareBankIntegratedWithdrawal** - -Prepare a bank-integrated withdrawal operation. - -Wallet-core resolves the ``taler://withdraw`` URI against the bank's -bank integration API, obtains the status of the withdrawal operation -and creates a withdrawal transaction in a dialog state. If the bank -suggests an exchange that is not known to the wallet yet, it is -ephemerally added to the wallet's known exchanges. - -After the user has reviewed the operation's details (and selected an -amount and an exchange, where the bank allows choosing them), the -client confirms the withdrawal with :ref:`confirmWithdrawal -<wallet-op-confirmWithdrawal>`. - -**Request:** - - The request must be a `PrepareBankIntegratedWithdrawalRequest` - object. - -**Response:** - - On success, the result is a `PrepareBankIntegratedWithdrawalResponse` - object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``. - -**Details:** - - The operation is idempotent with respect to the URI: calling it - again with the same ``talerWithdrawUri`` returns the existing - withdrawal transaction instead of creating a new one. If the bank - reports the withdrawal operation as already selected, confirmed or - aborted and the wallet has no transaction for it, the request fails. - -.. ts:def:: PrepareBankIntegratedWithdrawalRequest - - interface PrepareBankIntegratedWithdrawalRequest { - talerWithdrawUri: string; - - progressToken?: string; - } - -.. ts:def:: PrepareBankIntegratedWithdrawalResponse - - interface PrepareBankIntegratedWithdrawalResponse { - // Transaction ID of the withdrawal transaction (newly created or - // already existing for the same URI). - transactionId: TransactionIdStr; - - // Details of the bank-side withdrawal operation. - info: WithdrawUriInfoResponse; - } - -.. ts:def:: WithdrawUriInfoResponse - - interface WithdrawUriInfoResponse { - operationId: string; - status: WithdrawalOperationStatusFlag; - - // URL for confirming the transfer at the bank. - confirmTransferUrl?: string; - - currency: string; - - // Amount that will be withdrawn (raw amount, without fee - // considerations). Only given once the amount is fixed. - amount: AmountString | undefined; - - // Set to true if the user is allowed to edit the amount. - // Note that even with a non-editable amount, the amount might be - // undefined at the beginning of the withdrawal process. - editableAmount: boolean; - - // Maximum amount that the wallet can choose to withdraw. - maxAmount: AmountString | undefined; - - wireFee: AmountString | undefined; - - // Exchange suggested by the bank, if any. - defaultExchangeBaseUrl?: string; - - editableExchange: boolean; - - // Exchanges that can be used for the withdrawal, filtered to the - // currency of the withdrawal operation. If the exchange is not - // editable, contains only the bank-suggested exchange. - possibleExchanges: ExchangeListItem[]; - } - -.. ts:def:: WithdrawalOperationStatusFlag - - // Status of a bank-integrated withdrawal operation: - // - "pending": pending parameter selection (exchange and reserve - // public key) - // - "selected": parameters selected, pending confirmation - // - "aborted": the operation has been aborted - // - "confirmed": the transfer has been confirmed and registered by - // the bank - type WithdrawalOperationStatusFlag = - | "pending" - | "selected" - | "aborted" - | "confirmed"; - - -.. _wallet-op-confirmWithdrawal: - -**confirmWithdrawal** - -Confirm a withdrawal transaction. - -Confirms a bank-integrated withdrawal that was previously prepared -with :ref:`prepareBankIntegratedWithdrawal -<wallet-op-prepareBankIntegratedWithdrawal>`, selecting the exchange -to withdraw from and, where the withdrawal operation has an editable -amount, the amount to withdraw. The exchange's terms of service must -have been accepted and any pending exchange key change must have been -confirmed beforehand. - -After the confirmation, wallet-core registers the reserve with the -bank; the user then authorizes the actual wire transfer to the -exchange in their banking application. - -**Request:** - - The request must be a `ConfirmWithdrawalRequest` object. - -**Response:** - - On success, the result is an empty object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_KYC_LIMIT_EXCEEDED``, ``WALLET_NO_SUITABLE_EXCHANGE``, - ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, ``WALLET_TRANSACTION_NOT_FOUND``, - ``GENERIC_CURRENCY_MISMATCH``, ``WALLET_CORE_API_BAD_REQUEST``, - ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. - -**Details:** - - The ``transactionId`` must refer to a withdrawal transaction that is - still in the dialog state. Confirming is idempotent: once the - withdrawal has been registered with the bank, repeating the - confirmation succeeds without further effect. - - The ``amount`` may only be omitted for withdrawals from a foreign - account (a ``taler://withdraw`` URI with - ``external-confirmation=1``), where the bank fixes the amount when - the transfer is confirmed. - -.. ts:def:: ConfirmWithdrawalRequest - - interface ConfirmWithdrawalRequest { - transactionId: string; - exchangeBaseUrl: string; - amount?: AmountString | undefined; - forcedDenomSel?: ForcedDenomSel; - restrictAge?: number; - - progressToken?: string; - } - -.. ts:def:: ForcedDenomSel - - interface ForcedDenomSel { - denoms: { - value: AmountString; - count: number; - }[]; - } - - -.. _wallet-op-acceptBankIntegratedWithdrawal: - -**acceptBankIntegratedWithdrawal** - -This operation is **deprecated**. Use -:ref:`prepareBankIntegratedWithdrawal -<wallet-op-prepareBankIntegratedWithdrawal>` followed by -:ref:`confirmWithdrawal <wallet-op-confirmWithdrawal>` instead. - -Accept a bank-integrated withdrawal in one step, equivalent to -preparing the withdrawal and then confirming it with the given -exchange and amount. Before returning, the wallet tries to register -the reserve with the bank. Thus, after this call returns, the -withdrawal operation can be confirmed with the bank via -``confirmTransferUrl``. - -**Request:** - - The request must be an `AcceptBankIntegratedWithdrawalRequest` - object. - -**Response:** - - On success, the result is an `AcceptWithdrawalResponse` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. - -.. ts:def:: AcceptBankIntegratedWithdrawalRequest - - interface AcceptBankIntegratedWithdrawalRequest { - talerWithdrawUri: string; - exchangeBaseUrl: string; - forcedDenomSel?: ForcedDenomSel; - - // Amount to withdraw. If the bank's withdrawal operation uses a - // fixed amount, this field must either be left undefined or its - // value must match the amount from the withdrawal operation. - amount?: AmountString; - - restrictAge?: number; - - progressToken?: string; - } - -.. ts:def:: AcceptWithdrawalResponse - - interface AcceptWithdrawalResponse { - confirmTransferUrl?: string; - transactionId: TransactionIdStr; - } - - -.. _wallet-op-getWithdrawalDetailsForAmount: - -**getWithdrawalDetailsForAmount** - -Get details for withdrawing a particular amount (manual withdrawal). - -Computes the terms of withdrawing the given amount: the raw amount the -user has to transfer to the exchange, the effective amount that will be -added to the wallet balance after withdrawal fees, the number of coins -that would be withdrawn, the exchange's bank accounts that can receive -the transfer (including accounts that require currency conversion), -age-restriction options and a preview of KYC requirements. - -No transaction is created. The client uses the result to let the user -review the withdrawal before creating it with -:ref:`acceptManualWithdrawal <wallet-op-acceptManualWithdrawal>`. - -**Request:** - - The request must be a `GetWithdrawalDetailsForAmountRequest` object. - -**Response:** - - On success, the result is a `WithdrawalDetailsForAmount` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, - ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, ``GENERIC_CURRENCY_MISMATCH``. - -**Details:** - - The exchange is selected with ``exchangeBaseUrl``. When it is - omitted, ``restrictScope`` names a currency scope and the wallet's - preferred exchange for that scope is used instead. - - When ``transactionId`` refers to a prepared bank-integrated - withdrawal, the sender account of that withdrawal is taken into - account when evaluating account-specific withdrawal rules (KYC). - - An ``unconfirmedKeyChange`` in the result means the exchange changed - its key set and the user has not confirmed the change yet; accepting - the withdrawal will be refused until the change is confirmed with - :ref:`confirmExchangeKeyChange <wallet-op-confirmExchangeKeyChange>`, - so this is the point at which to warn the user. - -.. ts:def:: GetWithdrawalDetailsForAmountRequest - - interface GetWithdrawalDetailsForAmountRequest { - exchangeBaseUrl?: string; - - // Prepared bank-integrated withdrawal whose sender account should - // be checked. - transactionId?: TransactionIdStr; - - // Specify currency scope for the withdrawal. - // May only be used when exchangeBaseUrl is not specified. - restrictScope?: ScopeInfo; - - amount: AmountString; - - restrictAge?: number; - - progressToken?: string; - } - -.. ts:def:: WithdrawalDetailsForAmount - - interface WithdrawalDetailsForAmount extends WithdrawalKycPreview { - // Exchange base URL for the withdrawal. - exchangeBaseUrl: string; - - // Amount that the user will transfer to the exchange. - amountRaw: AmountString; - - // Amount that will be added to the user's wallet balance. - amountEffective: AmountString; - - // Number of coins that would be used for withdrawal. - // UIs should warn if this number is too high (roughly at >100). - numCoins: number; - - // Ways to pay the exchange, including accounts that require - // currency conversion. - withdrawalAccountsList: WithdrawalExchangeAccountDetails[]; - - // If the exchange supports age-restricted coins it will return - // the array of ages. - ageRestrictionOptions?: number[]; - - // Scope info of the currency withdrawn. - scopeInfo: ScopeInfo; - - // Set when the exchange changed its key set and the user has not - // confirmed the change. Accepting the withdrawal will be refused - // until they do, so this is the point at which to warn them. - unconfirmedKeyChange?: ExchangeKeyChangeInfo; - - // KYC soft limit. - // Withdrawals over that amount will require KYC. - kycSoftLimit?: AmountString; - - // KYC hard limit. - // Withdrawals over that amount will be denied. - kycHardLimit?: AmountString; - - // Ways to pay the exchange. - // Deprecated in favor of withdrawalAccountsList. - paytoUris: string[]; - } - -.. ts:def:: WithdrawalKycPreview - - interface WithdrawalKycPreview { - // Whether the proposed withdrawal needs a KYC warning based on - // this wallet's balance, known KYC allowance, advertised - // zero-limit rules and known withdrawal volume. This is a - // preview, not a guarantee that the exchange will not require - // KYC. Optional for compatibility with older wallet-core - // versions, which omit it. - kycRequired?: boolean; - - // Balance usage from the same evaluation as kycRequired. - balanceKyc?: BalanceKycUsage; - - // Account-specific withdrawal-rule preview using this wallet's - // history. "ok" only covers exposed rules and available local - // history; "unknown" means account limits could not be evaluated - // and must not be shown as clearance. Older wallet-core versions - // omit this field. - withdrawalKycStatus?: WithdrawalKycStatus; - } - -.. ts:def:: BalanceKycUsage - - // This wallet's balance at the issuing exchange at preview time, - // using the same accounting as balance-KYC enforcement (including - // pending refresh outputs). Does not reserve capacity for - // concurrent withdrawals or report account-wide - // transaction-volume/hard-limit usage. - interface BalanceKycUsage { - currentBalance: AmountString; - - // Current balance plus the selected coins' value after withdrawal - // fees. - projectedBalance: AmountString; - - // Applicable balance threshold; omitted when no finite limit is - // known. - threshold?: AmountString; - - // Additional balance permitted, clamped to zero; omitted with - // threshold. - remaining?: AmountString; - } - -.. ts:def:: WithdrawalKycStatus - - type WithdrawalKycStatus = - | "unknown" - | "ok" - | "kyc-required" - | "hard-limit"; - -.. ts:def:: WithdrawalExchangeAccountDetails - - interface WithdrawalExchangeAccountDetails { - // Payto URI of the exchange. Depending on whether the (manual!) - // withdrawal is accepted or just being checked, this already - // includes the subject with the reserve public key. - paytoUri: string; - - // Whether the account can be used by the user to send funds for a - // withdrawal. "ok": account should be shown to the user; - // "error": account should not be shown to the user, UIs might - // render the error (in conversionError), especially in dev mode. - status: "ok" | "error"; - - // Transfer amount. Might be in a different currency than the - // requested amount for withdrawal. Absent if this is a - // conversion account and the conversion failed. - transferAmount?: AmountString; - - // Currency specification for the external currency. - // Only included if this account requires a currency conversion. - currencySpecification?: CurrencySpecification; - - // Further restrictions for sending money to the exchange. - creditRestrictions?: AccountRestriction[]; - - // Label given to the account or the account's bank by the - // exchange. - bankLabel?: string; - - // Display priority assigned to this bank account by the exchange. - priority?: number; - - // Error that happened when attempting to request the conversion - // rate. - conversionError?: TalerErrorDetail; - - // Timestamp that indicates when the transfer options expire. - // If missing, options do not expire. - transferExpiry?: TalerProtocolTimestamp; - - // Options for transferring funds to the exchange for the - // withdrawal. - transferOptions: TransferOption[]; - } - -.. ts:def:: TransferOption - - type TransferOption = - | TransferOptionPayto - | TransferOptionUri - | TransferOptionSwissQrBill; - -.. ts:def:: TransferOptionPayto - - interface TransferOptionPayto { - type: "payto"; - paytoUri: string; - qrCodes: QrCodeSpec[]; - } - -.. ts:def:: TransferOptionUri - - interface TransferOptionUri { - type: "uri"; - uri: string; - } - -.. ts:def:: TransferOptionSwissQrBill - - interface TransferOptionSwissQrBill { - type: "ch-qr-bill"; - paytoUri: string; - qrReferenceNumber: string; - qrCodes: QrCodeSpec[]; - } - - -.. _wallet-op-acceptManualWithdrawal: - -**acceptManualWithdrawal** - -Create a manual withdrawal. - -Creates a withdrawal transaction for the given amount at the given -exchange, with a freshly generated reserve key pair. The user must -then wire the amount to one of the exchange's bank accounts returned -in the result; the ``paytoUri`` of each account already includes the -reserve public key as the transfer subject. Once the transfer -arrives at the exchange, wallet-core withdraws the coins. - -**Request:** - - The request must be an `AcceptManualWithdrawalRequest` object. - -**Response:** - - On success, the result is an `AcceptManualWithdrawalResult` object. - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_KYC_LIMIT_EXCEEDED``, ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, - ``GENERIC_CURRENCY_MISMATCH``, ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. - -.. ts:def:: AcceptManualWithdrawalRequest - - interface AcceptManualWithdrawalRequest { - exchangeBaseUrl: string; - amount: AmountString; - restrictAge?: number; - - // Instead of generating a fresh, random reserve key pair, use - // the provided reserve private key. Use with caution. Usage of - // this field may be restricted to developer mode. - forceReservePriv?: EddsaPrivateKeyString; - - progressToken?: string; - } - -.. ts:def:: AcceptManualWithdrawalResult - - interface AcceptManualWithdrawalResult { - // Transaction ID of the newly created withdrawal transaction. - transactionId: TransactionIdStr; - - // Public key of the newly created reserve. - reservePub: string; - - // Bank accounts of the exchange that can be used to fund the - // withdrawal. - withdrawalAccountsList: WithdrawalExchangeAccountDetails[]; - } - - -.. _wallet-op-getWithdrawalDetailsForUri: - -**getWithdrawalDetailsForUri** - -This operation is **deprecated**. Use -:ref:`prepareBankIntegratedWithdrawal -<wallet-op-prepareBankIntegratedWithdrawal>` instead. - -Get details for withdrawing via a ``taler://withdraw`` URI, without -creating a withdrawal transaction. As side effects, the bank is -queried via the bank integration API and the exchange suggested by the -bank is ephemerally added to the wallet's list of known exchanges. - -**Request:** - - The request must be a `GetWithdrawalDetailsForUriRequest` object. - -**Response:** - - On success, the result is a `WithdrawUriInfoResponse` object (see - :ref:`prepareBankIntegratedWithdrawal - <wallet-op-prepareBankIntegratedWithdrawal>`). - -**Expected errors:** - - The caller can handle the following errors inline: - ``WALLET_TALER_URI_MALFORMED``. - -.. ts:def:: GetWithdrawalDetailsForUriRequest - - interface GetWithdrawalDetailsForUriRequest { - talerWithdrawUri: string; - - // Deprecated, not used. - restrictAge?: number; - - progressToken?: string; - } diff --git a/core/wallet-core/withdrawals/accept-bank-integrated-withdrawal.rst b/core/wallet-core/withdrawals/accept-bank-integrated-withdrawal.rst @@ -0,0 +1,58 @@ +.. ts:op:: acceptBankIntegratedWithdrawal + :deprecated: + + This operation is **deprecated**. Use + :ts:op:`prepareBankIntegratedWithdrawal` followed by + :ts:op:`confirmWithdrawal` instead. + + Accept a bank-integrated withdrawal in one step, equivalent to + preparing the withdrawal and then confirming it with the given + exchange and amount. Before returning, the wallet tries to register + the reserve with the bank. Thus, after this call returns, the + withdrawal operation can be confirmed with the bank via + ``confirmTransferUrl``. + + **Request:** + + The request must be an `AcceptBankIntegratedWithdrawalRequest` + object. + + **Response:** + + On success, the result is an `AcceptWithdrawalResponse` object. + + **Side effects:** + + Combines the side effects of :ts:op:`prepareBankIntegratedWithdrawal` + and :ts:op:`confirmWithdrawal`: creates (or reuses) the withdrawal + transaction and starts the task that registers the reserve with the + bank and withdraws the coins. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. + +.. ts:def:: AcceptBankIntegratedWithdrawalRequest + + interface AcceptBankIntegratedWithdrawalRequest { + talerWithdrawUri: string; + exchangeBaseUrl: string; + forcedDenomSel?: ForcedDenomSel; + + // Amount to withdraw. If the bank's withdrawal operation uses a + // fixed amount, this field must either be left undefined or its + // value must match the amount from the withdrawal operation. + amount?: AmountString; + + restrictAge?: number; + + progressToken?: string; + } + +.. ts:def:: AcceptWithdrawalResponse + + interface AcceptWithdrawalResponse { + confirmTransferUrl?: string; + transactionId: TransactionIdStr; + } diff --git a/core/wallet-core/withdrawals/accept-manual-withdrawal.rst b/core/wallet-core/withdrawals/accept-manual-withdrawal.rst @@ -0,0 +1,61 @@ +.. ts:op:: acceptManualWithdrawal + + Create a manual withdrawal. + + Creates a withdrawal transaction for the given amount at the given + exchange, with a freshly generated reserve key pair. The user must + then wire the amount to one of the exchange's bank accounts returned + in the result; the ``paytoUri`` of each account already includes the + reserve public key as the transfer subject. Once the transfer + arrives at the exchange, wallet-core withdraws the coins. + + **Request:** + + The request must be an `AcceptManualWithdrawalRequest` object. + + **Response:** + + On success, the result is an `AcceptManualWithdrawalResult` object. + + **Side effects:** + + Creates the withdrawal transaction (with a fresh reserve key pair) in + the wallet database and starts the background task that watches the + reserve and withdraws the coins once the transfer arrives. May + refresh exchange information and query the bank conversion service + over the network. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_KYC_LIMIT_EXCEEDED``, ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, + ``GENERIC_CURRENCY_MISMATCH``, ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. + +.. ts:def:: AcceptManualWithdrawalRequest + + interface AcceptManualWithdrawalRequest { + exchangeBaseUrl: string; + amount: AmountString; + restrictAge?: number; + + // Instead of generating a fresh, random reserve key pair, use + // the provided reserve private key. Use with caution. Usage of + // this field may be restricted to developer mode. + forceReservePriv?: EddsaPrivateKeyString; + + progressToken?: string; + } + +.. ts:def:: AcceptManualWithdrawalResult + + interface AcceptManualWithdrawalResult { + // Transaction ID of the newly created withdrawal transaction. + transactionId: TransactionIdStr; + + // Public key of the newly created reserve. + reservePub: string; + + // Bank accounts of the exchange that can be used to fund the + // withdrawal. + withdrawalAccountsList: WithdrawalExchangeAccountDetails[]; + } diff --git a/core/wallet-core/withdrawals/confirm-withdrawal.rst b/core/wallet-core/withdrawals/confirm-withdrawal.rst @@ -0,0 +1,72 @@ +.. ts:op:: confirmWithdrawal + + Confirm a withdrawal transaction. + + Confirms a bank-integrated withdrawal that was previously prepared + with :ts:op:`prepareBankIntegratedWithdrawal`, selecting the exchange + to withdraw from and, where the withdrawal operation has an editable + amount, the amount to withdraw. The exchange's terms of service must + have been accepted and any pending exchange key change must have been + confirmed beforehand. + + After the confirmation, the user authorizes the actual wire transfer + to the exchange in their banking application. + + **Request:** + + The request must be a `ConfirmWithdrawalRequest` object. + + **Response:** + + On success, the result is an empty object. + + **Side effects:** + + Updates the withdrawal transaction in the wallet database and starts + the background task that registers the reserve with the bank and + withdraws the coins. May talk to the exchange, the bank integration + API and the bank conversion service over the network; when an amount + is given, denomination verification results are stored in the + database. A sender bank account that is new to the wallet is added + to its known bank accounts. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_KYC_LIMIT_EXCEEDED``, ``WALLET_NO_SUITABLE_EXCHANGE``, + ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, ``WALLET_TRANSACTION_NOT_FOUND``, + ``GENERIC_CURRENCY_MISMATCH``, ``WALLET_CORE_API_BAD_REQUEST``, + ``WALLET_EXCHANGE_KEYS_NOT_ACCEPTED``. + + **Details:** + + The ``transactionId`` must refer to a withdrawal transaction that is + still in the dialog state. Confirming is idempotent: once the + withdrawal has been registered with the bank, repeating the + confirmation succeeds without further effect. + + The ``amount`` may only be omitted for withdrawals from a foreign + account (a ``taler://withdraw`` URI with + ``external-confirmation=1``), where the bank fixes the amount when + the transfer is confirmed. + +.. ts:def:: ConfirmWithdrawalRequest + + interface ConfirmWithdrawalRequest { + transactionId: string; + exchangeBaseUrl: string; + amount?: AmountString | undefined; + forcedDenomSel?: ForcedDenomSel; + restrictAge?: number; + + progressToken?: string; + } + +.. ts:def:: ForcedDenomSel + + interface ForcedDenomSel { + denoms: { + value: AmountString; + count: number; + }[]; + } diff --git a/core/wallet-core/withdrawals/get-withdrawal-details-for-amount.rst b/core/wallet-core/withdrawals/get-withdrawal-details-for-amount.rst @@ -0,0 +1,246 @@ +.. ts:op:: getWithdrawalDetailsForAmount + + Get details for withdrawing a particular amount (manual withdrawal). + + Computes the terms of withdrawing the given amount: the raw amount + the user has to transfer to the exchange, the effective amount that + will be added to the wallet balance after withdrawal fees, the number + of coins that would be withdrawn, the exchange's bank accounts that + can receive the transfer (including accounts that require currency + conversion), age-restriction options and a preview of KYC + requirements. + + The client uses the result to let the user review the withdrawal + before creating it with :ts:op:`acceptManualWithdrawal`. + + **Request:** + + The request must be a `GetWithdrawalDetailsForAmountRequest` object. + + **Response:** + + On success, the result is a `WithdrawalDetailsForAmount` object. + + **Side effects:** + + May refresh the exchange's key material over the network, validates + withdrawal denominations and stores the verification results in the + database, and queries the bank conversion service for accounts that + require currency conversion. No transaction is created. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_EXCHANGE_ENTRY_NOT_FOUND``, + ``WALLET_EXCHANGE_TOS_NOT_ACCEPTED``, ``GENERIC_CURRENCY_MISMATCH``. + + **Details:** + + The exchange is selected with ``exchangeBaseUrl``. When it is + omitted, ``restrictScope`` names a currency scope and the wallet's + preferred exchange for that scope is used instead. + + When ``transactionId`` refers to a prepared bank-integrated + withdrawal, the sender account of that withdrawal is taken into + account when evaluating account-specific withdrawal rules (KYC). + + An ``unconfirmedKeyChange`` in the result means the exchange changed + its key set and the user has not confirmed the change yet; accepting + the withdrawal will be refused until the change is confirmed with + :ts:op:`confirmExchangeKeyChange`, so this is the point at which to + warn the user. + +.. ts:def:: GetWithdrawalDetailsForAmountRequest + + interface GetWithdrawalDetailsForAmountRequest { + exchangeBaseUrl?: string; + + // Prepared bank-integrated withdrawal whose sender account should + // be checked. + transactionId?: TransactionIdStr; + + // Specify currency scope for the withdrawal. + // May only be used when exchangeBaseUrl is not specified. + restrictScope?: ScopeInfo; + + amount: AmountString; + + restrictAge?: number; + + progressToken?: string; + } + +.. ts:def:: WithdrawalDetailsForAmount + + interface WithdrawalDetailsForAmount extends WithdrawalKycPreview { + // Exchange base URL for the withdrawal. + exchangeBaseUrl: string; + + // Amount that the user will transfer to the exchange. + amountRaw: AmountString; + + // Amount that will be added to the user's wallet balance. + amountEffective: AmountString; + + // Number of coins that would be used for withdrawal. + // UIs should warn if this number is too high (roughly at >100). + numCoins: number; + + // Ways to pay the exchange, including accounts that require + // currency conversion. + withdrawalAccountsList: WithdrawalExchangeAccountDetails[]; + + // If the exchange supports age-restricted coins it will return + // the array of ages. + ageRestrictionOptions?: number[]; + + // Scope info of the currency withdrawn. + scopeInfo: ScopeInfo; + + // Set when the exchange changed its key set and the user has not + // confirmed the change. Accepting the withdrawal will be refused + // until they do, so this is the point at which to warn them. + unconfirmedKeyChange?: ExchangeKeyChangeInfo; + + // KYC soft limit. + // Withdrawals over that amount will require KYC. + kycSoftLimit?: AmountString; + + // KYC hard limit. + // Withdrawals over that amount will be denied. + kycHardLimit?: AmountString; + + // Ways to pay the exchange. + // Deprecated in favor of withdrawalAccountsList. + paytoUris: string[]; + } + +.. ts:def:: WithdrawalKycPreview + + interface WithdrawalKycPreview { + // Whether the proposed withdrawal needs a KYC warning based on + // this wallet's balance, known KYC allowance, advertised + // zero-limit rules and known withdrawal volume. This is a + // preview, not a guarantee that the exchange will not require + // KYC. Optional for compatibility with older wallet-core + // versions, which omit it. + kycRequired?: boolean; + + // Balance usage from the same evaluation as kycRequired. + balanceKyc?: BalanceKycUsage; + + // Account-specific withdrawal-rule preview using this wallet's + // history. "ok" only covers exposed rules and available local + // history; "unknown" means account limits could not be evaluated + // and must not be shown as clearance. Older wallet-core versions + // omit this field. + withdrawalKycStatus?: WithdrawalKycStatus; + } + +.. ts:def:: BalanceKycUsage + + // This wallet's balance at the issuing exchange at preview time, + // using the same accounting as balance-KYC enforcement (including + // pending refresh outputs). Does not reserve capacity for + // concurrent withdrawals or report account-wide + // transaction-volume/hard-limit usage. + interface BalanceKycUsage { + currentBalance: AmountString; + + // Current balance plus the selected coins' value after withdrawal + // fees. + projectedBalance: AmountString; + + // Applicable balance threshold; omitted when no finite limit is + // known. + threshold?: AmountString; + + // Additional balance permitted, clamped to zero; omitted with + // threshold. + remaining?: AmountString; + } + +.. ts:def:: WithdrawalKycStatus + + type WithdrawalKycStatus = + | "unknown" + | "ok" + | "kyc-required" + | "hard-limit"; + +.. ts:def:: WithdrawalExchangeAccountDetails + + interface WithdrawalExchangeAccountDetails { + // Payto URI of the exchange. Depending on whether the (manual!) + // withdrawal is accepted or just being checked, this already + // includes the subject with the reserve public key. + paytoUri: string; + + // Whether the account can be used by the user to send funds for a + // withdrawal. "ok": account should be shown to the user; + // "error": account should not be shown to the user, UIs might + // render the error (in conversionError), especially in dev mode. + status: "ok" | "error"; + + // Transfer amount. Might be in a different currency than the + // requested amount for withdrawal. Absent if this is a + // conversion account and the conversion failed. + transferAmount?: AmountString; + + // Currency specification for the external currency. + // Only included if this account requires a currency conversion. + currencySpecification?: CurrencySpecification; + + // Further restrictions for sending money to the exchange. + creditRestrictions?: AccountRestriction[]; + + // Label given to the account or the account's bank by the + // exchange. + bankLabel?: string; + + // Display priority assigned to this bank account by the exchange. + priority?: number; + + // Error that happened when attempting to request the conversion + // rate. + conversionError?: TalerErrorDetail; + + // Timestamp that indicates when the transfer options expire. + // If missing, options do not expire. + transferExpiry?: TalerProtocolTimestamp; + + // Options for transferring funds to the exchange for the + // withdrawal. + transferOptions: TransferOption[]; + } + +.. ts:def:: TransferOption + + type TransferOption = + | TransferOptionPayto + | TransferOptionUri + | TransferOptionSwissQrBill; + +.. ts:def:: TransferOptionPayto + + interface TransferOptionPayto { + type: "payto"; + paytoUri: string; + qrCodes: QrCodeSpec[]; + } + +.. ts:def:: TransferOptionUri + + interface TransferOptionUri { + type: "uri"; + uri: string; + } + +.. ts:def:: TransferOptionSwissQrBill + + interface TransferOptionSwissQrBill { + type: "ch-qr-bill"; + paytoUri: string; + qrReferenceNumber: string; + qrCodes: QrCodeSpec[]; + } diff --git a/core/wallet-core/withdrawals/get-withdrawal-details-for-uri.rst b/core/wallet-core/withdrawals/get-withdrawal-details-for-uri.rst @@ -0,0 +1,90 @@ +.. ts:op:: getWithdrawalDetailsForUri + :deprecated: + + This operation is **deprecated**. Use + :ts:op:`prepareBankIntegratedWithdrawal` instead. + + Get details for withdrawing via a ``taler://withdraw`` URI, without + creating a withdrawal transaction. + + **Request:** + + The request must be a `GetWithdrawalDetailsForUriRequest` object. + + **Response:** + + On success, the result is a `WithdrawUriInfoResponse` object (see + :ts:op:`prepareBankIntegratedWithdrawal`). + + **Side effects:** + + Queries the bank's bank integration API over the network; the + exchange suggested by the bank may be fetched and ephemerally added + to the wallet's known exchanges. No transaction is created. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``. + +.. ts:def:: WithdrawUriInfoResponse + + interface WithdrawUriInfoResponse { + operationId: string; + status: WithdrawalOperationStatusFlag; + + // URL for confirming the transfer at the bank. + confirmTransferUrl?: string; + + currency: string; + + // Amount that will be withdrawn (raw amount, without fee + // considerations). Only given once the amount is fixed. + amount: AmountString | undefined; + + // Set to true if the user is allowed to edit the amount. + // Note that even with a non-editable amount, the amount might be + // undefined at the beginning of the withdrawal process. + editableAmount: boolean; + + // Maximum amount that the wallet can choose to withdraw. + maxAmount: AmountString | undefined; + + wireFee: AmountString | undefined; + + // Exchange suggested by the bank, if any. + defaultExchangeBaseUrl?: string; + + editableExchange: boolean; + + // Exchanges that can be used for the withdrawal, filtered to the + // currency of the withdrawal operation. If the exchange is not + // editable, contains only the bank-suggested exchange. + possibleExchanges: ExchangeListItem[]; + } + +.. ts:def:: WithdrawalOperationStatusFlag + + // Status of a bank-integrated withdrawal operation: + // - "pending": pending parameter selection (exchange and reserve + // public key) + // - "selected": parameters selected, pending confirmation + // - "aborted": the operation has been aborted + // - "confirmed": the transfer has been confirmed and registered by + // the bank + type WithdrawalOperationStatusFlag = + | "pending" + | "selected" + | "aborted" + | "confirmed"; + +.. ts:def:: GetWithdrawalDetailsForUriRequest + + interface GetWithdrawalDetailsForUriRequest { + talerWithdrawUri: string; + + // Deprecated, not used. + restrictAge?: number; + + progressToken?: string; + } diff --git a/core/wallet-core/withdrawals/prepare-bank-integrated-withdrawal.rst b/core/wallet-core/withdrawals/prepare-bank-integrated-withdrawal.rst @@ -0,0 +1,61 @@ +.. ts:op:: prepareBankIntegratedWithdrawal + + Prepare a bank-integrated withdrawal operation. + + Wallet-core resolves the ``taler://withdraw`` URI against the bank's + bank integration API, obtains the status of the withdrawal operation + and creates a withdrawal transaction in a dialog state. If the bank + suggests an exchange that is not known to the wallet yet, it is + ephemerally added to the wallet's known exchanges. + + After the user has reviewed the operation's details (and selected an + amount and an exchange, where the bank allows choosing them), the + client confirms the withdrawal with :ts:op:`confirmWithdrawal`. + + **Request:** + + The request must be a `PrepareBankIntegratedWithdrawalRequest` + object. + + **Response:** + + On success, the result is a `PrepareBankIntegratedWithdrawalResponse` + object. + + **Side effects:** + + Queries the bank's bank integration API over the network and creates + the withdrawal transaction in the wallet database. May also fetch + and ephemerally add the exchange suggested by the bank. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``. + + **Details:** + + The operation is idempotent with respect to the URI: calling it + again with the same ``talerWithdrawUri`` returns the existing + withdrawal transaction instead of creating a new one. If the bank + reports the withdrawal operation as already selected, confirmed or + aborted and the wallet has no transaction for it, the request fails. + +.. ts:def:: PrepareBankIntegratedWithdrawalRequest + + interface PrepareBankIntegratedWithdrawalRequest { + talerWithdrawUri: string; + + progressToken?: string; + } + +.. ts:def:: PrepareBankIntegratedWithdrawalResponse + + interface PrepareBankIntegratedWithdrawalResponse { + // Transaction ID of the withdrawal transaction (newly created or + // already existing for the same URI). + transactionId: TransactionIdStr; + + // Details of the bank-side withdrawal operation. + info: WithdrawUriInfoResponse; + } diff --git a/core/wallet-core/withdrawals/prepare-withdraw-exchange.rst b/core/wallet-core/withdrawals/prepare-withdraw-exchange.rst @@ -0,0 +1,54 @@ +.. ts:op:: prepareWithdrawExchange + + Prepare for withdrawing via a ``taler://withdraw-exchange`` URI. + + The URI names the exchange to withdraw from and may fix an amount. + Wallet-core fetches (or updates) the exchange entry, ephemerally + adding the exchange to the wallet's known exchanges if it is not + known yet, and checks that the currency of the URI's amount matches + the exchange's currency. + + This is the first step of a manual withdrawal that is initiated on + the exchange's side. The client can then query the fees and other + details with :ts:op:`getWithdrawalDetailsForAmount` and create the + withdrawal with :ts:op:`acceptManualWithdrawal`. + + **Request:** + + The request must be a `PrepareWithdrawExchangeRequest` object. + + **Response:** + + On success, the result is a `PrepareWithdrawExchangeResponse` object. + + **Side effects:** + + Fetches (or updates) the exchange entry over the network; if the + exchange is not known to the wallet yet, it is ephemerally added to + the known exchanges. No withdrawal transaction is created. + + **Expected errors:** + + The caller can handle the following errors inline: + ``WALLET_TALER_URI_MALFORMED``, ``GENERIC_CURRENCY_MISMATCH``. + +.. ts:def:: PrepareWithdrawExchangeRequest + + interface PrepareWithdrawExchangeRequest { + // A taler://withdraw-exchange URI. + talerUri: string; + + progressToken?: string; + } + +.. ts:def:: PrepareWithdrawExchangeResponse + + interface PrepareWithdrawExchangeResponse { + // Base URL of the exchange that already existed or was + // ephemerally added as an exchange entry to the wallet. + exchangeBaseUrl: string; + + // Amount from the taler://withdraw-exchange URI. + // Only present if specified in the URI. + amount?: AmountString; + }