summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/api-auditor.rst257
-rw-r--r--core/api-bank-access.rst290
-rw-r--r--core/api-bank-conversion-info.rst223
-rw-r--r--core/api-bank-integration.rst153
-rw-r--r--core/api-bank-revenue.rst (renamed from core/api-bank-merchant.rst)84
-rw-r--r--core/api-bank-wire.rst (renamed from core/api-wire.rst)156
-rw-r--r--core/api-c2ec.rst342
-rw-r--r--core/api-challenger.rst353
-rw-r--r--core/api-common.rst245
-rw-r--r--core/api-corebank.rst1234
-rw-r--r--core/api-donau.rst615
-rw-r--r--core/api-exchange.rst2157
-rw-r--r--core/api-mailbox.rst3
-rw-r--r--core/api-merchant.rst2000
-rw-r--r--core/api-overview.rst150
-rw-r--r--core/api-sync.rst10
-rw-r--r--core/api-taldir.rst2
-rw-r--r--core/index-bank-apis.rst (renamed from core/api-error.rst)35
-rw-r--r--core/index.rst192
-rw-r--r--core/intro-bank-apis.rst134
-rw-r--r--core/tos.rst7
21 files changed, 6099 insertions, 2543 deletions
diff --git a/core/api-auditor.rst b/core/api-auditor.rst
index 321d203e..61e448b8 100644
--- a/core/api-auditor.rst
+++ b/core/api-auditor.rst
@@ -24,6 +24,25 @@ for all details not specified in the individual requests.
The `glossary <https://docs.taler.net/glossary.html#glossary>`_
defines all specific terms used in this section.
+.. contents:: Table of Contents
+ :local:
+
+.. _authentication:
+
+--------------
+Authentication
+--------------
+
+Each auditor instance has separate authentication settings for the private API resources
+of that instance.
+
+Currently, the API supports two main authentication methods:
+
+* ``external``: With this method, no checks are done by the auditor backend.
+ Instead, a reverse proxy / API gateway must do all authentication/authorization checks.
+* ``token``: With this method, the client must provide a ``Authorization: Bearer $TOKEN``
+ header, where ``$TOKEN`` is a secret authentication token configured for the instance which must begin with the RFC 8959 prefix.
+
.. _auditor-version:
-------------------------
@@ -34,9 +53,10 @@ This API is used by merchants to obtain a list of all exchanges audited by
this auditor. This may be required for the merchant to perform the required
know-your-customer (KYC) registration before issuing contracts.
-.. http:get:: /version
+.. http:get:: /config
Get the protocol version and some meta data about the auditor.
+ This specification corresponds to ``current`` protocol being version **1**.
**Response:**
@@ -56,11 +76,19 @@ know-your-customer (KYC) registration before issuing contracts.
// protocol is versioned independently of the exchange's protocol.
version: string;
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+
// Return which currency this auditor is auditing for.
currency: string;
// EdDSA master public key of the auditor.
auditor_public_key: EddsaPublicKey;
+
+ // EdDSA master public key of the exchange.
+ // Added in protocol v1.
+ exchange_master_public_key: EddsaPublicKey;
}
.. note::
@@ -69,58 +97,63 @@ know-your-customer (KYC) registration before issuing contracts.
time of this writing).
-.. _exchange-list:
+.. _exchange_signkeys-list:
------------------------
-Obtaining Exchange List
------------------------
+------------------------------------
+Obtaining Exchange Signing Keys List
+------------------------------------
-This API is used by merchants to obtain a list of all exchanges audited by
-this auditor. This may be required for the merchant to perform the required
-know-your-customer (KYC) registration before issuing contracts.
+This API is used by auditor to obtain a list of all exchanges signing keys to be audited.
-.. http:get:: /exchanges
+.. http:get:: /exchange-sign-keys
- Get a list of all exchanges audited by the auditor.
+ Get a list of all exchange signing keys to be audited by the auditor.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`ExchangeList` object. This request should
+ The auditor responds with a :ts:type:`ExchangeSignKeysList` object. This request should
virtually always be successful.
**Details:**
- .. ts:def:: ExchangeList
+ .. ts:def:: ExchangeSignKeysList
- interface ExchangeList {
- // Exchanges audited by this auditor.
- exchanges: ExchangeEntry[];
+ interface ExchangeSignKeysList {
+ // Exchange signing keys to be audited by the auditor.
+ exchange-sign-key: ExchangeSignKeyEntry[];
}
- .. ts:def:: ExchangeEntry
+ .. ts:def:: ExchangeSignKeyEntry
- interface ExchangeEntry {
+ interface ExchangeSignKeyEntry {
- // Master public key of the exchange.
- master_pub: EddsaPublicKey;
+ // Public online signing key of the exchange.
+ exchange_pub: EddsaPublicKey;
// Base URL of the exchange.
- exchange_url: string;
+ master_sig: EddsaSignature;
+
+ // Time when online signing key will first be use.
+ ep_valid_from: Timestamp;
+
+ // Time when this online signing key will no longer be used.
+ ep_expire_sign: Timestamp;
+
+ // Time when this online signing key legally expires.
+ ep_expire_legal: Timestamp;
}
.. note::
This API is still experimental (and is not yet implemented at the
- time of this writing). A key open question is whether the auditor
- should sign the information. We might also want to support more
- delta downloads in the future.
+ time of this writing).
.. _deposit-confirmation:
---------------------------------
-Submitting deposit confirmations
---------------------------------
+---------------------
+Deposit Confirmations
+---------------------
Merchants should probabilistically submit some of the deposit
confirmations they receive from the exchange to auditors to ensure
@@ -182,10 +215,12 @@ paid out first.
// amount with fee and the fee from the deposit request.
amount_without_fee: Amount;
- // The coin's public key. This is the value that must have been
- // signed (blindly) by the Exchange. The deposit request is to be
- // signed by the corresponding private key (using EdDSA).
- coin_pub: CoinPublicKey;
+ // Array of public keys of the deposited coins.
+ coin_pubs: EddsaPublicKey[];
+
+ // Array of deposit signatures of the deposited coins.
+ // Must have the same length as ``coin_pubs``.
+ coin_sigs: EddsaSignature[];
// The Merchant's public key. Allows the merchant to later refund
// the transaction or to inquire about the wire transfer identifier.
@@ -200,6 +235,7 @@ paid out first.
// Master public key of the exchange corresponding to ``master_sig``.
// Identifies the exchange this is about.
+ // @deprecated since v1 (now ignored, global per auditor)
master_pub: EddsaPublicKey;
// When does the validity of the exchange_pub end?
@@ -221,6 +257,167 @@ paid out first.
time of this writing). A key open question is whether the auditor
should sign the response information.
+This API is used by the auditor to obtain a list of all deposit confirmations that the auditor
+did not receive by the exchange, only by the merchant.
+
+.. http:get:: /deposit-confirmation
+
+ Get a list of all deposit confirmations that were not received by the auditor from the exchange to be manually audited.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a :ts:type:`DepositConfirmationList` object.
+ :http:statuscode:`204 No Content`:
+ No missing deposit confirmations found.
+
+ **Details:**
+
+ .. ts:def:: DepositConfirmationList
+
+ interface DepositConfirmationList {
+ // Deposit confirmations to be audited.
+ deposit-confirmations: DepositConfirmation[];
+ }
+
+ .. ts:def:: DepositConfirmation
+
+ interface DepositConfirmation {
+
+ // Database row id of entry
+ row_id: Integer;
+
+ // Time when the deposit confirmation confirmation was generated.
+ timestamp: Timestamp;
+
+ // How much time does the merchant have to issue a refund
+ // request? Zero if refunds are not allowed.
+ refund_deadline: Timestamp;
+
+ // By what time does the exchange have to wire the funds?
+ wire_deadline: Timestamp;
+
+ // Amount to be deposited, excluding fee. Calculated from the
+ // amount with fee and the fee from the deposit request.
+ amount_without_fee: Amount;
+ }
+
+ .. note::
+
+ This API is still experimental (and is not yet implemented at the
+ time of this writing).
+
+This API is used by the auditor to delete an audited deposit confirmation.
+
+.. http:delete:: /deposit-confirmation/$SERIAL_ID
+
+ Delete deposit confirmation entry with given serial_id.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The deposit confirmation was deleted.
+
+ :http:statuscode:`401 Unauthorized`:
+ Unauthorized request.
+
+ :http:statuscode:`404 Not found`:
+ The deposit confirmation was unknown.
+
+ .. note::
+
+ This API is still experimental (and is not yet implemented at the
+ time of this writing).
+
+.. balances-list:
+
+----------------------------------
+Obtaining list of auditor balances
+----------------------------------
+
+This API is used to obtain a list of all the balances that are stored by the auditor.
+
+.. http:get:: /balances
+
+ Get a list of all balances stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a :ts:type:`BalanceList` object.
+
+ :http:statuscode:`409 Conflict`:
+ Balance missing or other error occured.
+
+ **Details:**
+
+ .. ts:def:: BalanceList
+
+ interface BalanceList {
+ // Total amount reported
+ auditor_total_reported_balance: Amount;
+
+ // Amount potentially missing
+ auditor_missing_balance: Amount;
+
+ //...
+ }
+
+ .. note::
+
+ This API is still experimental (and is not yet implemented at the
+ time of this writing). The API will be further developed as needed.
+
+.. denominations-pending-list:
+
+---------------------------------------
+Obtaining list of pending denominations
+---------------------------------------
+
+This API is used by the auditor to obtain a list of pending denominations
+
+.. http:get:: /pending-denominations
+
+ Get a list of all pending denominations.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a :ts:type:`PendingDenominationList` object.
+
+ :http:statuscode:`204 No content`:
+ No pending demoninations found.
+
+ **Details:**
+
+ .. ts:def:: PendingDenominationList
+
+ interface PendingDenominationList {
+ // PendingDenominationList of the auditor.
+ pending_denomination: PendingDenomination[];
+ }
+
+ .. ts:def:: PendingDenomination
+
+ interface PendingDenomination {
+
+ // Balance of denomination.
+ denom_balance: Amount;
+
+ // Amount that was lost due to failures by the exchange.
+ denom_loss: Amount;
+
+ // Amount at risk of loss due to recoup operations.
+ denom_risk: Amount;
+
+ // Amount actually lost due to recoup operations after a revocation.
+ recoup_loss: Amount;
+ }
+
+ .. note::
+
+ This API is still experimental (and is not yet implemented at the
+ time of this writing).
----------
Complaints
diff --git a/core/api-bank-access.rst b/core/api-bank-access.rst
deleted file mode 100644
index 42003556..00000000
--- a/core/api-bank-access.rst
+++ /dev/null
@@ -1,290 +0,0 @@
-..
- This file is part of GNU TALER.
-
- Copyright (C) 2014-2020 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 2.1, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-
- @author Florian Dold
-
-=====================
-Taler Bank Access API
-=====================
-
-This chapter describes the API that the GNU Taler demonstrator bank offers to access accounts.
-
-This API differs from the "Bank Integration API" in that it provides advanced API access to accounts, as opposed
-to enabling wallets to withdraw with a better user experience ("tight integration").
-
-
-------------------------
-Accounts and Withdrawals
-------------------------
-
-.. http:get:: ${BANK_API_BASE_URL}/public-accounts
-
- Show those accounts whose histories are publicly visible. For example,
- accounts from donation receivers. As such, this request is unauthenticated.
-
- **Response**
-
- **Details**
-
- .. ts:def:: PublicAccountsResponse
-
- interface PublicAccountsResponse {
- publicAccounts: PublicAccount[]
- }
-
- .. ts:def:: PublicAccount
-
- interface PublicAccount {
- iban: string;
- balance: string;
- // The account name _and_ the username of the
- // Sandbox customer that owns such a bank account.
- accountLabel: string;
- }
-
-The following endpoints require HTTP "Basic" authentication with the account
-name and account password, at least in the GNU Taler demo bank implementation.
-
-
-.. http:get:: ${BANK_API_BASE_URL}/accounts/${account_name}
-
- Request the current balance of an account. (New: ) In case of a public bank
- account, no authentication is required.
-
- **Response**
-
- **Details**
-
- .. ts:def:: BankAccountBalanceResponse
-
- interface Balance {
- amount: Amount;
- credit_debit_indicator: "credit" | "debit";
- }
-
- interface BankAccountBalanceResponse {
- // Available balance on the account.
- balance: Balance;
-
- // payto://-URI of the account. (New)
- paytoUri: string;
-
- // Number indicating the max debit allowed for the requesting user.
- debitThreshold: String;
- }
-
-
-.. http:post:: ${BANK_API_BASE_URL}/accounts/${account_name}/withdrawals
-
- Create a withdrawal operation, resulting in a ``taler://withdraw`` URI.
-
- **Request**
-
- .. ts:def:: BankAccountCreateWithdrawalRequest
-
- interface BankAccountCreateWithdrawalRequest {
- // Amount to withdraw.
- amount: Amount;
- }
-
- **Response**
-
- .. ts:def:: BankAccountCreateWithdrawalResponse
-
- interface BankAccountCreateWithdrawalResponse {
- // ID of the withdrawal, can be used to view/modify the withdrawal operation.
- withdrawal_id: string;
-
- // URI that can be passed to the wallet to initiate the withdrawal.
- taler_withdraw_uri: string;
- }
-
- :http:statuscode:`403 Forbidden`:
- The operation was rejected due to insufficient funds.
-
-.. http:get:: ${BANK_API_BASE_URL}/accounts/${account_name}/withdrawals/${withdrawal_id}
-
- Query the status of a withdrawal operation.
-
- **Response**
-
- **Details**
-
- .. ts:def:: BankAccountGetWithdrawalResponse
-
- interface BankAccountGetWithdrawalResponse {
- // Amount that will be withdrawn with this withdrawal operation.
- amount: Amount;
-
- // Was the withdrawal aborted?
- aborted: boolean;
-
- // Has the withdrawal been confirmed by the bank?
- // The wire transfer for a withdrawal is only executed once
- // both ``confirmation_done`` is ``true`` and ``selection_done`` is ``true``.
- confirmation_done: boolean;
-
- // Did the wallet select reserve details?
- selection_done: boolean;
-
- // Reserve public key selected by the exchange,
- // only non-null if ``selection_done`` is ``true``.
- selected_reserve_pub: string | null;
-
- // Exchange account selected by the wallet, or by the bank
- // (with the default exchange) in case the wallet did not provide one
- // through the Integration API.
- selected_exchange_account: string | null;
- }
-
-
-.. http:post:: ${BANK_API_BASE_URL}/accounts/${account_name}/withdrawals/${withdrawal_id}/abort
-
- Abort a withdrawal operation. Has no effect on an already aborted withdrawal operation.
-
- :http:statuscode:`200 OK`: The withdrawal operation has been aborted. The response is an empty JSON object.
- :http:statuscode:`409 Conflict`: The reserve operation has been confirmed previously and can't be aborted.
-
-
-.. http:post:: ${BANK_API_BASE_URL}/accounts/${account_name}/withdrawals/${withdrawal_id}/confirm
-
- Confirm a withdrawal operation. Has no effect on an already confirmed withdrawal operation.
- This call is responsible of wiring the funds to the exchange.
-
- **Response**
-
- :http:statuscode:`200 OK`:
- The withdrawal operation has been confirmed. The response is an empty JSON object.
- :http:statuscode:`409 Conflict`:
- The withdrawal has been aborted previously and can't be confirmed.
- :http:statuscode:`422 Unprocessable Entity` (New):
- The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before.
-
-------------
-Transactions
-------------
-
-.. http:get:: ${BANK_API_BASE_URL}/accounts/${account_name}/transactions
-
- Retrieve a subset of transactions related to $account_name. Without
- query parameters, it returns the last 5 transactions.
-
- **Request**
-
- :query page: page number starting from 1. Page 1 has the latest transactions
- and 1 is the default value.
- :query size: how many transactions per page, defaults to 5.
- :query from_ms: Optional. Filters the results to transactions *from* this
- timestamp in milliseconds
- :query until_ms: Optional. Filters the results to transactions *until* this
- timestamp in milliseconds
-
- **Response**
-
- .. ts:def:: BankAccountTransactionsResponse
-
- interface BankAccountTransactionsResponse {
- transactions: BankAccountTransactionInfo[];
- }
-
-.. http:get:: ${BANK_API_BASE_URL}/accounts/${account_name}/transactions/${transaction_id}
-
- **Response**
-
- Retrieve the transaction whose identifier is ``transaction_id``,
- in the following format:
-
- .. ts:def:: BankAccountTransactionInfo
-
- interface BankAccountTransactionInfo {
-
- creditorIban: string;
- creditorBic: string; // Optional
- creditorName: string;
-
- debtorIban: string;
- debtorBic: string;
- debtorName: string;
-
- amount: number;
- currency: string;
- subject: string;
-
- // Transaction unique ID. Matches
- // $transaction_id from the URI.
- uid: string;
- direction: "DBIT" | "CRDT";
- date: string; // milliseconds since the Unix epoch
- }
-
-
-.. http:post:: ${BANK_API_BASE_URL}/accounts/${account_name}/transactions
-
- Create a new transaction where the bank account with the label ``account_name`` is **debited**.
-
- **Request**
-
- .. ts:def:: BankAccountTransactionCreate
-
- interface CreateBankAccountTransactionCreate {
-
- // Address in the Payto format of the wire transfer receiver.
- // It needs at least the 'message' query string parameter.
- paytoUri: string;
-
- // Transaction amount (in the $currency:x.y format), optional.
- // However, when not given, its value must occupy the 'amount'
- // query string parameter of the 'payto' field. In case it
- // is given in both places, the paytoUri's takes the precedence.
- amount: string;
- }
-
- **Response**
-
- :http:statuscode:`200 OK`:
- the transaction has been created.
-
- :http:statuscode:`400 Bad Request`:
- the request was invalid or the payto://-URI used unacceptable features.
-
-.. http:delete:: ${BANK_API_BASE_URL}/accounts/${account_name}
-
- Delete the bank account (and the customer entry) from the database.
- Note, customer usernames and bank accounts have the same value.
-
-----------------------
-Registration (Testing)
-----------------------
-
-.. http:post:: ${BANK_API_BASE_URL}/testing/register
-
- Create a new bank account. This endpoint should be disabled for most deployments, but is useful
- for automated testing / integration tests.
-
- **Request**
-
- .. ts:def:: BankRegistrationRequest
-
- interface BankRegistrationRequest {
- username: string;
-
- password: string;
- }
-
-
- **Response**
-
- :http:statuscode:`200 OK`: Registration was successful.
diff --git a/core/api-bank-conversion-info.rst b/core/api-bank-conversion-info.rst
new file mode 100644
index 00000000..322e9403
--- /dev/null
+++ b/core/api-bank-conversion-info.rst
@@ -0,0 +1,223 @@
+..
+ This file is part of GNU TALER.
+
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Marcello Stanisci
+ @author Christian Grothoff
+ @author Florian Dold
+
+=========================
+Taler Conversion Info API
+=========================
+
+This chapter describes the conversion info API. The conversion info API
+is used by wallets for withdrawals that involve a currency conversion.
+
+
+.. contents:: Table of Contents
+
+.. http:get:: /config
+
+ Get configuration information about the bank.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `ConversionConfig`.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: ConversionConfig
+
+ interface ConversionConfig {
+ // libtool-style representation of the Bank protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Name of the API.
+ name: "taler-conversion-info";
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v4, may become mandatory in the future.
+ implementation?: string;
+
+ // Currency used by this bank.
+ regional_currency: string;
+
+ // How the bank SPA should render this currency.
+ regional_currency_specification: CurrencySpecification;
+
+ // External currency used during conversion.
+ fiat_currency: string;
+
+ // How the bank SPA should render this currency.
+ fiat_currency_specification: CurrencySpecification;
+
+ // Additional information on conversion rates.
+ // Those informations should never be used to perform conversions,
+ // use /cashin-rate or /cashout-rate instead.
+ // Conversion rates can change at any time. Clients must deal with
+ // any resulting errors and call /cashin-rate or /cashout-rate again
+ // to use the new rates.
+ conversion_rate: ConversionRate;
+ }
+
+
+.. http:get:: /cashin-rate
+
+ This public endpoint allows clients to calculate
+ the exchange rate between the regional currency
+ and the fiat currency of the banking system.
+
+ This endpoint shows how the bank would apply the cash-in
+ ratio and fee to one input amount. Typically, wallets would
+ request this endpoint before creating withdrawals that involve
+ a currency conversion.
+
+ **Request:**
+
+ :query amount_debit: this is the amount that the user will get
+ deducted from their fiat bank account.
+
+ or
+
+ :query amount_credit: this is the amount that the user will receive
+ in their regional bank account.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `CashinConversionResponse`.
+ :http:statuscode:`400 Bad request`:
+ * ``TALER_EC_GENERIC_PARAMETER_MISSING`` : none of the parameters have been provided.
+ * ``TALER_EC_GENERIC_PARAMETER_MALFORMED`` : both of the parameters have been provided or one of them is not a valid Taler amount.
+ * ``TALER_EC_GENERIC_CURRENCY_MISMATCH`` : the parameter is in the wrong currency.
+ :http:statuscode:`409 Conflict`:
+ The amount is too small to be converted, either because it produces produce an amount less than zero, or because the server requires a higher minimum amount than that supplied.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: CashinConversionResponse
+
+ interface CashinConversionResponse {
+ // Amount that the user will get deducted from their fiat
+ // bank account, according to the 'amount_credit' value.
+ amount_debit: Amount;
+ // Amount that the user will receive in their regional
+ // bank account, according to 'amount_debit'.
+ amount_credit: Amount;
+ }
+
+.. http:get:: /cashout-rate
+
+ This public endpoint allows clients to calculate
+ the exchange rate between the regional currency
+ and the fiat currency of the banking system.
+
+ This endpoint shows how the bank would apply the cash-out
+ ratio and fee to one input amount. Typically, frontends
+ ask this endpoint before creating cash-in operations.
+
+ **Request:**
+
+ :query amount_debit: this is the amount that the user will get
+ deducted from their regional bank account.
+
+ or
+
+ :query amount_credit: this is the amount that the user will receive
+ in their fiat bank account.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `CashoutConversionResponse`.
+ :http:statuscode:`400 Bad request`:
+ * ``TALER_EC_GENERIC_PARAMETER_MISSING`` : none of the parameters have been provided.
+ * ``TALER_EC_GENERIC_PARAMETER_MALFORMED`` : both of the parameters have been provided or one of them is not a valid Taler amount.
+ * ``TALER_EC_GENERIC_CURRENCY_MISMATCH`` : the parameter is in the wrong currency.
+ :http:statuscode:`409 Conflict`:
+ The amount is too small to be converted, either because it produces produce an amount less than zero, or because the server requires a higher minimum amount than that supplied.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: CashoutConversionResponse
+
+ interface CashoutConversionResponse {
+ // Amount that the user will get deducted from their regional
+ // bank account, according to the 'amount_credit' value.
+ amount_debit: Amount;
+ // Amount that the user will receive in their fiat
+ // bank account, according to 'amount_debit'.
+ amount_credit: Amount;
+ }
+
+.. http:post:: /conversion-rate
+
+ This endpoint allows the administrator to update
+ the exchange rate between the regional currency
+ and the fiat currency of the banking system.
+
+ **Request:**
+
+ .. ts:def:: ConversionRate
+
+ interface ConversionRate {
+ // Exchange rate to buy regional currency from fiat
+ cashin_ratio: DecimalNumber;
+
+ // Regional amount fee to subtract after applying the cashin ratio.
+ cashin_fee: Amount;
+
+ // Minimum fiat amount authorised for cashin before conversion
+ cashin_min_amount: Amount;
+
+ // Smallest possible regional amount, converted amount is rounded to this amount
+ cashin_tiny_amount: Amount;
+
+ // Rounding mode used during cashin conversion
+ cashin_rounding_mode: "zero" | "up" | "nearest";
+
+ // Exchange rate to sell regional currency for fiat
+ cashout_ratio: DecimalNumber;
+
+ // Fiat amount fee to subtract after applying the cashout ratio.
+ cashout_fee: Amount;
+
+ // Minimum regional amount authorised for cashout before conversion
+ cashout_min_amount: Amount;
+
+ // Smallest possible fiat amount, converted amount is rounded to this amount
+ cashout_tiny_amount: Amount;
+
+ // Rounding mode used during cashout conversion
+ cashout_rounding_mode: "zero" | "up" | "nearest";
+ }
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ Operation successful.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
diff --git a/core/api-bank-integration.rst b/core/api-bank-integration.rst
index 5ed98be1..4131a28a 100644
--- a/core/api-bank-integration.rst
+++ b/core/api-bank-integration.rst
@@ -1,7 +1,7 @@
..
This file is part of GNU TALER.
- Copyright (C) 2014-2020 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -28,31 +28,37 @@ to tightly integrate with GNU Taler.
.. http:get:: /config
- Get configuration information about the bank.
-
- **Request:**
+ Return the protocol version and configuration information about the bank.
+ This specification corresponds to ``current`` protocol being version **2**.
**Response:**
:http:statuscode:`200 OK`:
- The exchange responds with a `BankVersion` object. This request should
+ The exchange responds with a `IntegrationConfig` object. This request should
virtually always be successful.
**Details:**
- .. ts:def:: BankVersion
+ .. ts:def:: IntegrationConfig
+
+ interface IntegrationConfig {
+ // Name of the API.
+ name: "taler-bank-integration";
- interface BankVersion {
// libtool-style representation of the Bank protocol version, see
// https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
// The format is "current:revision:age".
version: string;
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v2, may become mandatory in the future.
+ implementation?: string;
+
// Currency used by this bank.
currency: string;
- // Name of the API.
- name: "taler-bank-integration";
+ // How the bank SPA should render this currency.
+ currency_specification: CurrencySpecification;
}
@@ -63,41 +69,42 @@ Withdrawing
Withdrawals with a Taler-integrated bank are based on withdrawal operations.
Some user interaction (on the bank's website or a Taler-enabled ATM) creates a
withdrawal operation record in the bank's database. The wallet can use a unique identifier
-for the withdrawal operation (the ``wopid``) to interact with the withdrawal operation.
+for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdrawal operation.
-.. http:get:: ${BANK_API_BASE_URL}/withdrawal-operation/${wopid}
+.. http:get:: /withdrawal-operation/$WITHDRAWAL_ID
- Query information about a withdrawal operation, identified by the ``wopid``.
+ Query information about a withdrawal operation, identified by the ``WITHDRAWAL_ID``.
- **Request**
+ **Request:**
:query long_poll_ms:
*Optional.* If specified, the bank will wait up to ``long_poll_ms``
- milliseconds for completion of the transfer before sending the HTTP
+ milliseconds for operationt state to be different from ``old_state`` before sending the HTTP
response. A client must never rely on this behavior, as the bank may
return a response immediately.
+ :query old_state:
+ *Optional.* Default to "pending".
- **Response**
+ **Response:**
:http:statuscode:`200 OK`:
The withdrawal operation is known to the bank, and details are given
in the `BankWithdrawalOperationStatus` response body.
+ :http:statuscode:`404 Not found`:
+ The operation was not found
+ **Details:**
.. ts:def:: BankWithdrawalOperationStatus
export class BankWithdrawalOperationStatus {
- // Indicates whether the withdrawal was aborted.
- aborted: boolean;
-
- // Has the wallet selected parameters for the withdrawal operation
- // (exchange and reserve public key) and successfully sent it
- // to the bank?
- selection_done: boolean;
-
- // The transfer has been confirmed and registered by the bank.
- // Does not guarantee that the funds have arrived at the exchange already.
- transfer_done: boolean;
+ // Current status of the operation
+ // pending: the operation is pending parameters selection (exchange and reserve public key)
+ // selected: the operations has been selected and is pending confirmation
+ // aborted: the operation has been aborted
+ // confirmed: the transfer has been confirmed and registered by the bank
+ // Since protocol v1.
+ status: "pending" | "selected" | "aborted" | "confirmed";
// Amount that will be withdrawn with this operation
// (raw amount without fee considerations).
@@ -112,53 +119,103 @@ for the withdrawal operation (the ``wopid``) to interact with the withdrawal ope
// URL that the user needs to navigate to in order to
// complete some final confirmation (e.g. 2FA).
+ // Only applicable when ``status`` is ``selected`` or ``pending``.
+ // It may contain withdrawal operation id
confirm_transfer_url?: string;
// Wire transfer types supported by the bank.
wire_types: string[];
- }
-.. http:post:: ${BANK_API_BASE_URL}/withdrawal-operation/${wopid}
+ // Reserve public key selected by the exchange,
+ // only non-null if ``status`` is ``selected`` or ``confirmed``.
+ // Since protocol v1.
+ selected_reserve_pub?: string;
- **Request** The body of this request must have the format of a `BankWithdrawalOperationPostRequest`.
+ // Exchange account selected by the wallet
+ // only non-null if ``status`` is ``selected`` or ``confirmed``.
+ // Since protocol v1.
+ selected_exchange_account?: string;
- **Response**
+ // Deprecated field since protocol v1 use ``status`` instead
+ // Indicates whether the withdrawal was aborted.
+ aborted: boolean;
- :http:statuscode:`200 OK`:
- The bank has accepted the withdrawal operation parameters chosen by the wallet.
- The response is a `BankWithdrawalOperationPostResponse`.
- :http:statuscode:`404 Not found`:
- The bank does not know about a withdrawal operation with the specified ``wopid``.
- :http:statuscode:`409 Conflict` (New):
- The wallet selected a different exchange or reserve public key under the same withdrawal ID.
+ // Deprecated field since protocol v1 use ``status`` instead
+ // Has the wallet selected parameters for the withdrawal operation
+ // (exchange and reserve public key) and successfully sent it
+ // to the bank?
+ selection_done: boolean;
+
+ // Deprecated field since protocol v1 use ``status`` instead
+ // The transfer has been confirmed and registered by the bank.
+ // Does not guarantee that the funds have arrived at the exchange already.
+ transfer_done: boolean;
+ }
- **Details**
+.. http:post:: /withdrawal-operation/$WITHDRAWAL_ID
+
+ **Request:**
.. ts:def:: BankWithdrawalOperationPostRequest
interface BankWithdrawalOperationPostRequest {
-
// Reserve public key.
reserve_pub: string;
- // Exchange bank details specified in the ``payto``
- // format. NOTE: this field is optional, therefore
- // the bank will initiate the withdrawal with the
- // default exchange, if not given.
+ // Payto address of the exchange selected for the withdrawal.
selected_exchange: string;
}
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The bank has accepted the withdrawal operation parameters chosen by the wallet.
+ The response is a `BankWithdrawalOperationPostResponse`.
+ :http:statuscode:`404 Not found`:
+ The bank does not know about a withdrawal operation with the specified ``WITHDRAWAL_ID``.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT`` :
+ The wallet selected a different exchange or reserve public key under the same withdrawal ID.
+ * ``TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT`` : the reserve public key is already used.
+ * ``TALER_EC_BANK_UNKNOWN_ACCOUNT`` : the selected exchange account was not found.
+ * ``TALER_EC_BANK_ACCOUNT_IS_NOT_EXCHANGE`` : the selected account is not an exchange.
+
+ **Details:**
+
.. ts:def:: BankWithdrawalOperationPostResponse
interface BankWithdrawalOperationPostResponse {
-
- // The transfer has been confirmed and registered by the bank.
- // Does not guarantee that the funds have arrived at the exchange already.
- transfer_done: boolean;
+ // Current status of the operation
+ // pending: the operation is pending parameters selection (exchange and reserve public key)
+ // selected: the operations has been selected and is pending confirmation
+ // aborted: the operation has been aborted
+ // confirmed: the transfer has been confirmed and registered by the bank
+ status: "selected" | "aborted" | "confirmed";
// URL that the user needs to navigate to in order to
// complete some final confirmation (e.g. 2FA).
//
- // Only applicable when ``transfer_done`` is ``false``.
+ // Only applicable when ``status`` is ``selected`` or ``pending``.
+ // It may contain withdrawal operation id
confirm_transfer_url?: string;
+
+ // Deprecated field use ``status`` instead
+ // The transfer has been confirmed and registered by the bank.
+ // Does not guarantee that the funds have arrived at the exchange already.
+ transfer_done: boolean;
}
+
+.. http:post:: /withdrawal-operation/$WITHDRAWAL_ID/abort
+
+ Aborts ``WITHDRAWAL_ID`` operation. Has no effect on an already aborted
+ operation.
+ Since protocol v2.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The withdrawal operation has been aborted.
+ :http:statuscode:`404 Not found`:
+ The withdrawal operation was not found.
+ :http:statuscode:`409 Conflict`:
+ The withdrawal operation has been confirmed previously and can't be aborted.
diff --git a/core/api-bank-merchant.rst b/core/api-bank-revenue.rst
index 2277c088..98b38113 100644
--- a/core/api-bank-merchant.rst
+++ b/core/api-bank-revenue.rst
@@ -1,6 +1,6 @@
..
This file is part of GNU TALER.
- Copyright (C) 2021 Taler Systems SA
+ Copyright (C) 2021-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -13,15 +13,46 @@
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-============================
-Taler Bank Merchant HTTP API
-============================
+.. _taler-bank-merchant-http-api:
-This section describes an API offered by the Taler wire gateway. The API is
-used by the merchant to query for incoming transactions.
+===========================
+Taler Bank Revenue HTTP API
+===========================
-This API is TO BE implemented by the Taler Demo Bank, as well as by
-LibEuFin (work in progress).
+This section describes an API offered by libeufin-nexus and libeufin-bank. The API is
+used by the merchant (or other parties) to query for incoming transactions to their account.
+
+.. http:get:: /config
+
+ Return the protocol version and configuration information about the bank.
+ This specification corresponds to ``current`` protocol being version **0**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `RevenueConfig` object. This request should
+ virtually always be successful.
+
+ **Details:**
+
+ .. ts:def:: RevenueConfig
+
+ interface RevenueConfig {
+ // Name of the API.
+ name: "taler-revenue";
+
+ // libtool-style representation of the Bank protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Currency used by this gateway.
+ currency: string;
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+ }
--------------
@@ -35,17 +66,11 @@ The bank library authenticates requests to the bank merchant API using
Querying the transaction history
--------------------------------
+.. http:get:: /history
-.. http:get:: ${BASE_URL}/history
+ Return a list of transactions made to an account.
- Return a list of transactions made from an exchange to the merchant.
-
- Incoming transactions must contain a valid wire transfer identifier and
- exchange base URL. If a bank transaction does not conform to the right
- syntax, the wire gateway must not report it to the merchant via this
- endpoint.
-
- The bank account of the merchant is determined via the base URL and/or the
+ The bank account is determined via the base URL and/or the
user name in the ``Authorization`` header. In fact, the transaction history
might come from a "virtual" account, where multiple real bank accounts are
merged into one history.
@@ -85,7 +110,7 @@ Querying the transaction history
**Response:**
:http:statuscode:`200 OK`:
- JSON object of type `MerchantIncomingHistory`.
+ JSON object of type `RevenueIncomingHistory`.
:http:statuscode:`400 Bad request`:
Request malformed. The bank replies with an `ErrorDetail` object.
:http:statuscode:`401 Unauthorized`:
@@ -93,19 +118,23 @@ Querying the transaction history
:http:statuscode:`404 Not found`:
The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
- .. ts:def:: MerchantIncomingHistory
+ **Details:**
- interface MerchantIncomingHistory {
+ .. ts:def:: RevenueIncomingHistory
+ interface RevenueIncomingHistory {
// Array of incoming transactions.
- incoming_transactions : MerchantIncomingBankTransaction[];
+ incoming_transactions : RevenueIncomingBankTransaction[];
+ // Payto URI to identify the receiver of funds.
+ // Credit account is shared by all incoming transactions
+ // as per the nature of the request.
+ credit_account: string;
}
- .. ts:def:: MerchantIncomingBankTransaction
-
- interface MerchantIncomingBankTransaction {
+ .. ts:def:: RevenueIncomingBankTransaction
+ interface RevenueIncomingBankTransaction {
// Opaque identifier of the returned record.
row_id: SafeUint64;
@@ -118,9 +147,6 @@ Querying the transaction history
// Payto URI to identify the sender of funds.
debit_account: string;
- // Base URL of the exchange where the transfer originated form.
- exchange_url: string;
-
- // The wire transfer identifier.
- wtid: WireTransferIdentifierRawP;
+ // The wire transfer subject.
+ subject: string;
}
diff --git a/core/api-wire.rst b/core/api-bank-wire.rst
index 5ac3a7aa..a76f5195 100644
--- a/core/api-wire.rst
+++ b/core/api-bank-wire.rst
@@ -1,6 +1,6 @@
..
This file is part of GNU TALER.
- Copyright (C) 2019-2021 Taler Systems SA
+ Copyright (C) 2019-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -13,6 +13,8 @@
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+.. _taler-wire-gateway-http-api:
+
===========================
Taler Wire Gateway HTTP API
===========================
@@ -24,6 +26,37 @@ well as by the auditor to query incoming and outgoing transactions.
This API is currently implemented by the Taler Demo Bank, as well as by
LibEuFin (work in progress).
+.. http:get:: /config
+
+ Return the protocol version and configuration information about the bank.
+ This specification corresponds to ``current`` protocol being version **0**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `WireConfig` object. This request should
+ virtually always be successful.
+
+ **Details:**
+
+ .. ts:def:: WireConfig
+
+ interface WireConfig {
+ // Name of the API.
+ name: "taler-wire-gateway";
+
+ // libtool-style representation of the Bank protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Currency used by this gateway.
+ currency: string;
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+ }
--------------
Authentication
@@ -36,7 +69,7 @@ The bank library authenticates requests to the wire gateway via
Making Transactions
-------------------
-.. http:post:: ${BASE_URL}/transfer
+.. http:post:: /transfer
This API allows the exchange to make a transaction, typically to a merchant. The bank account
of the exchange is not included in the request, but instead derived from the user name in the
@@ -45,7 +78,31 @@ Making Transactions
To make the API idempotent, the client must include a nonce. Requests with the same nonce
are rejected unless the request is the same.
- **Request:** The body of this request must have the format of a `TransferRequest`.
+ **Request:**
+
+ .. ts:def:: TransferRequest
+
+ interface TransferRequest {
+ // Nonce to make the request idempotent. Requests with the same
+ // ``request_uid`` that differ in any of the other fields
+ // are rejected.
+ request_uid: HashCode;
+
+ // Amount to transfer.
+ amount: Amount;
+
+ // Base URL of the exchange. Shall be included by the bank gateway
+ // in the appropriate section of the wire transfer details.
+ exchange_base_url: string;
+
+ // Wire transfer identifier chosen by the exchange,
+ // used by the merchant to identify the Taler order(s)
+ // associated with this wire transfer.
+ wtid: ShortHashCode;
+
+ // The recipient's account identifier as a payto URI.
+ credit_account: string;
+ }
**Response:**
@@ -59,7 +116,7 @@ Making Transactions
:http:statuscode:`404 Not found`:
The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
:http:statuscode:`409 Conflict`:
- A transaction with the same ``transaction_uid`` but different transaction details
+ A transaction with the same ``request_uid`` but different transaction details
has been submitted before.
**Details:**
@@ -67,7 +124,6 @@ Making Transactions
.. ts:def:: TransferResponse
interface TransferResponse {
-
// Timestamp that indicates when the wire transfer will be executed.
// In cases where the wire transfer gateway is unable to know when
// the wire transfer will be executed, the time at which the request
@@ -82,37 +138,12 @@ Making Transactions
}
- .. ts:def:: TransferRequest
-
- interface TransferRequest {
- // Nonce to make the request idempotent. Requests with the same
- // ``transaction_uid`` that differ in any of the other fields
- // are rejected.
- request_uid: HashCode;
-
- // Amount to transfer.
- amount: Amount;
-
- // Base URL of the exchange. Shall be included by the bank gateway
- // in the appropriate section of the wire transfer details.
- exchange_base_url: string;
-
- // Wire transfer identifier chosen by the exchange,
- // used by the merchant to identify the Taler order(s)
- // associated with this wire transfer.
- wtid: ShortHashCode;
-
- // The recipient's account identifier as a payto URI.
- credit_account: string;
- }
-
-
--------------------------------
Querying the transaction history
--------------------------------
-.. http:get:: ${BASE_URL}/history/incoming
+.. http:get:: /history/incoming
Return a list of transactions made from or to the exchange.
@@ -170,13 +201,20 @@ Querying the transaction history
:http:statuscode:`404 Not found`:
The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ **Details:**
+
.. ts:def:: IncomingHistory
interface IncomingHistory {
-
// Array of incoming transactions.
incoming_transactions : IncomingBankTransaction[];
+ // Payto URI to identify the receiver of funds.
+ // This must be one of the exchange's bank accounts.
+ // Credit account is shared by all incoming transactions
+ // as per the nature of the request.
+ credit_account: string;
+
}
.. ts:def:: IncomingBankTransaction
@@ -200,10 +238,6 @@ Querying the transaction history
// Amount transferred.
amount: Amount;
- // Payto URI to identify the receiver of funds.
- // This must be one of the exchange's bank accounts.
- credit_account: string;
-
// Payto URI to identify the sender of funds.
debit_account: string;
@@ -241,7 +275,7 @@ Querying the transaction history
}
-.. http:get:: ${BASE_URL}/history/outgoing
+.. http:get:: /history/outgoing
Return a list of transactions made by the exchange, typically to a merchant.
@@ -295,19 +329,25 @@ Querying the transaction history
:http:statuscode:`404 Not found`:
The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ **Details:**
+
.. ts:def:: OutgoingHistory
interface OutgoingHistory {
-
// Array of outgoing transactions.
outgoing_transactions : OutgoingBankTransaction[];
+ // Payto URI to identify the sender of funds.
+ // This must be one of the exchange's bank accounts.
+ // Credit account is shared by all incoming transactions
+ // as per the nature of the request.
+ debit_account: string;
+
}
.. ts:def:: OutgoingBankTransaction
interface OutgoingBankTransaction {
-
// Opaque identifier of the returned record.
row_id: SafeUint64;
@@ -320,10 +360,6 @@ Querying the transaction history
// Payto URI to identify the receiver of funds.
credit_account: string;
- // Payto URI to identify the sender of funds.
- // This must be one of the exchange's bank accounts.
- debit_account: string;
-
// The wire transfer ID in the outgoing transaction.
wtid: ShortHashCode;
@@ -341,26 +377,12 @@ exposed by bank gateways in production.
.. _twg-admin-add-incoming:
-.. http:post:: ${BASE_URL}/admin/add-incoming
+.. http:post:: /admin/add-incoming
Simulate a transfer from a customer to the exchange. This API is *not*
idempotent since it's only used in testing.
- **Request:** The body of this request must have the format of a `AddIncomingRequest`.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The request has been correctly handled, so the funds have been transferred to
- the recipient's account. The body is a `AddIncomingResponse`.
- :http:statuscode:`400 Bad request`:
- The request is malformed. The bank replies with an `ErrorDetail` object.
- :http:statuscode:`401 Unauthorized`:
- Authentication failed, likely the credentials are wrong.
- :http:statuscode:`404 Not found`:
- The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
- :http:statuscode:`409 Conflict`:
- The 'reserve_pub' argument was used previously in another transfer, and the specification mandates that reserve public keys must not be reused.
+ **Request:**
.. ts:def:: AddIncomingRequest
@@ -379,11 +401,25 @@ exposed by bank gateways in production.
debit_account: string;
}
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request has been correctly handled, so the funds have been transferred to
+ the recipient's account. The body is a `AddIncomingResponse`.
+ :http:statuscode:`400 Bad request`:
+ The request is malformed. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`401 Unauthorized`:
+ Authentication failed, likely the credentials are wrong.
+ :http:statuscode:`404 Not found`:
+ The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`409 Conflict`:
+ The 'reserve_pub' argument was used previously in another transfer, and the specification mandates that reserve public keys must not be reused.
+
+ **Details:**
.. ts:def:: AddIncomingResponse
interface AddIncomingResponse {
-
// Timestamp that indicates when the wire transfer will be executed.
// In cases where the wire transfer gateway is unable to know when
// the wire transfer will be executed, the time at which the request
diff --git a/core/api-c2ec.rst b/core/api-c2ec.rst
new file mode 100644
index 00000000..88a6144a
--- /dev/null
+++ b/core/api-c2ec.rst
@@ -0,0 +1,342 @@
+..
+ This file is part of GNU TALER.
+
+ Copyright (C) 2014-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Joel Häberli
+
+===========================
+The C2EC RESTful API
+===========================
+
+.. note::
+
+ **This API is experimental and not yet implemented**
+
+This chapter describe the APIs that third party providers need to integrate to allow
+withdrawals through indirect payment channels like credit cards or ATM.
+
+.. contents:: Table of Contents
+
+--------------
+Authentication
+--------------
+
+Terminals which authenticate against the C2EC API must provide their respective
+access token. Therefore they provide a ``Authorization: Bearer $ACCESS_TOKEN`` header,
+where `$ACCESS_TOKEN`` is a secret authentication token configured by the exchange and
+must begin with the RFC 8959 prefix.
+
+----------------------------
+Configuration of C2EC
+----------------------------
+
+.. http:get:: /config
+
+ Return the protocol version and configuration information about the C2EC API.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `C2ECConfig` object. This request should
+ virtually always be successful.
+
+ **Details:**
+
+ .. ts:def:: C2ECConfig
+
+ interface C2ECConfig {
+ // Name of the API.
+ name: "taler-c2ec";
+
+ // libtool-style representation of the C2EC protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+ }
+
+-----------------------------
+Withdrawing using C2EC
+-----------------------------
+
+Withdrawals with a C2EC are based on withdrawal operations which register a withdrawal identifier
+(nonce) at the C2EC component. The provider must first create a unique identifier for the withdrawal
+operation (the ``WOPID``) to interact with the withdrawal operation and eventually withdraw using the wallet.
+
+.. http:post:: /withdrawal-operation
+
+ Register a `WOPID` belonging to a reserve public key.
+
+ **Request:**
+
+ .. ts:def:: C2ECWithdrawRegistration
+
+ interface C2ECWithdrawRegistration {
+ // Maps a nonce generated by the provider to a reserve public key generated by the wallet.
+ wopid: ShortHashCode;
+
+ // Reserve public key generated by the wallet.
+ // According to TALER_ReservePublicKeyP (https://docs.taler.net/core/api-common.html#cryptographic-primitives)
+ reserve_pub_key: EddsaPublicKey;
+
+ // Optional amount for the withdrawal.
+ amount?: Amount;
+
+ // Id of the terminal of the provider requesting a withdrawal by nonce.
+ // Assigned by the exchange.
+ terminal_id: SafeUint64;
+ }
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The withdrawal was successfully registered.
+ :http:statuscode:`400 Bad request`:
+ The ``WithdrawRegistration`` request was malformed or contained invalid parameters.
+ :http:statuscode:`500 Internal Server error`:
+ The registration of the withdrawal failed due to server side issues.
+
+.. http:get:: /withdrawal-operation/$WOPID
+
+ Query information about a withdrawal operation, identified by the ``WOPID``.
+
+ **Request:**
+
+ :query long_poll_ms:
+ *Optional.* If specified, the bank will wait up to ``long_poll_ms``
+ milliseconds for operationt state to be different from ``old_state`` before sending the HTTP
+ response. A client must never rely on this behavior, as the bank may
+ return a response immediately.
+ :query old_state:
+ *Optional.* Default to "pending".
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The withdrawal was found and is returned in the response body as ``C2ECWithdrawalStatus``.
+ :http:statuscode:`404 Not found`:
+ C2EC does not have a withdrawal registered with the specified ``WOPID``.
+
+ **Details**
+
+ .. ts:def:: C2ECWithdrawalStatus
+
+ interface C2ECWithdrawalStatus {
+ // Current status of the operation
+ // pending: the operation is pending parameters selection (exchange and reserve public key)
+ // selected: the operations has been selected and is pending confirmation
+ // aborted: the operation has been aborted
+ // confirmed: the transfer has been confirmed and registered by the bank
+ // Since protocol v1.
+ status: "pending" | "selected" | "aborted" | "confirmed";
+
+ // Amount that will be withdrawn with this operation
+ // (raw amount without fee considerations).
+ amount: Amount;
+
+ // A refund address as ``payto`` URI. This address shall be used
+ // in case a refund must be done. Only not-null if the status
+ // is "confirmed" or "aborted"
+ sender_wire?: string;
+
+ // Reserve public key selected by the exchange,
+ // only non-null if ``status`` is ``selected`` or ``confirmed``.
+ // Since protocol v1.
+ selected_reserve_pub?: string;
+ }
+
+
+.. http:post:: /withdrawal-operation/$WOPID
+
+ Notifies C2EC about an executed payment for a specific withdrawal.
+
+ **Request:**
+
+ .. ts:def:: C2ECPaymentNotification
+
+ interface C2ECPaymentNotification {
+
+ // Unique identifier of the provider transaction.
+ provider_transaction_id: string;
+
+ // Specifies the amount which was payed to the provider (without fees).
+ // This amount shall be put into the reserve linked to by the withdrawal id.
+ amount: Amount;
+
+ // Fees associated with the payment.
+ fees: Amount;
+ }
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ C2EC received the ``C2ECPaymentNotification`` successfully and will further process
+ the withdrawal.
+ :http:statuscode:`400 Bad request`:
+ The ``C2ECPaymentNotification`` request was malformed or contained invalid parameters.
+ :http:statuscode:`404 Not found`:
+ C2EC does not have a withdrawal registered with the specified ``WOPID``.
+ :http:statuscode:`500 Internal Server error`:
+ The ``C2ECPaymentNotification`` could not be processed due to server side issues.
+
+
+--------------
+Taler Wire Gateway
+--------------
+
+C2EC implements the wire gateway API in order to check for incoming transactions and
+let the exchange get proofs of payments. This will allow the C2EC componente to add reserves
+and therefore allow the withdrawal of the money. C2EC does not entirely implement all endpoints,
+because the it is not needed for the case of C2EC. The endpoints not implemented are not described
+further. They will be available but respond with 400 http error code.
+
+.. http:get:: /config
+
+ Return the protocol version and configuration information about the bank.
+ This specification corresponds to ``current`` protocol being version **0**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `WireConfig` object. This request should
+ virtually always be successful.
+
+ **Details:**
+
+ .. ts:def:: WireConfig
+
+ interface WireConfig {
+ // Name of the API.
+ name: "taler-wire-gateway";
+
+ // libtool-style representation of the Bank protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Currency used by this gateway.
+ currency: string;
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+ }
+
+.. http:post:: /transfer
+
+ This API allows the exchange to make a transaction, typically to a merchant. The bank account
+ of the exchange is not included in the request, but instead derived from the user name in the
+ authentication header and/or the request base URL.
+
+ To make the API idempotent, the client must include a nonce. Requests with the same nonce
+ are rejected unless the request is the same.
+
+ **Request:**
+
+ .. ts:def:: TransferRequest
+
+ interface TransferRequest {
+ // Nonce to make the request idempotent. Requests with the same
+ // ``request_uid`` that differ in any of the other fields
+ // are rejected.
+ request_uid: HashCode;
+
+ // Amount to transfer.
+ amount: Amount;
+
+ // Base URL of the exchange. Shall be included by the bank gateway
+ // in the appropriate section of the wire transfer details.
+ exchange_base_url: string;
+
+ // Wire transfer identifier chosen by the exchange,
+ // used by the merchant to identify the Taler order(s)
+ // associated with this wire transfer.
+ wtid: ShortHashCode;
+
+ // The recipient's account identifier as a payto URI.
+ credit_account: string;
+ }
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request has been correctly handled, so the funds have been transferred to
+ the recipient's account. The body is a `TransferResponse`.
+ :http:statuscode:`400 Bad request`:
+ Request malformed. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`401 Unauthorized`:
+ Authentication failed, likely the credentials are wrong.
+ :http:statuscode:`404 Not found`:
+ The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`409 Conflict`:
+ A transaction with the same ``request_uid`` but different transaction details
+ has been submitted before.
+
+ **Details:**
+
+ .. ts:def:: TransferResponse
+
+ interface TransferResponse {
+ // Timestamp that indicates when the wire transfer will be executed.
+ // In cases where the wire transfer gateway is unable to know when
+ // the wire transfer will be executed, the time at which the request
+ // has been received and stored will be returned.
+ // The purpose of this field is for debugging (humans trying to find
+ // the transaction) as well as for taxation (determining which
+ // time period a transaction belongs to).
+ timestamp: Timestamp;
+
+ // Opaque ID of the transaction that the bank has made.
+ row_id: SafeUint64;
+ }
+
+.. http:get:: /history/incoming
+
+ **Request:**
+
+ :query start: *Optional.*
+ Row identifier to explicitly set the *starting point* of the query.
+ :query delta:
+ The *delta* value that determines the range of the query.
+ :query long_poll_ms: *Optional.* If this parameter is specified and the
+ result of the query would be empty, the bank will wait up to ``long_poll_ms``
+ milliseconds for new transactions that match the query to arrive and only
+ then send the HTTP response. A client must never rely on this behavior, as
+ the bank may return a response immediately or after waiting only a fraction
+ of ``long_poll_ms``.
+
+ **Response:**
+
+ .. ts:def:: IncomingReserveTransaction
+
+ interface IncomingReserveTransaction {
+ type: "RESERVE";
+
+ // Opaque identifier of the returned record.
+ row_id: SafeUint64;
+
+ // Date of the transaction.
+ date: Timestamp;
+
+ // Amount transferred.
+ amount: Amount;
+
+ // Payto URI to identify the sender of funds.
+ debit_account: string;
+
+ // The reserve public key extracted from the transaction details.
+ reserve_pub: EddsaPublicKey;
+
+ }
+
diff --git a/core/api-challenger.rst b/core/api-challenger.rst
new file mode 100644
index 00000000..914d8d01
--- /dev/null
+++ b/core/api-challenger.rst
@@ -0,0 +1,353 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Christian Grothoff
+
+.. _challenger-api:
+
+======================
+Challenger Service API
+======================
+
+The challenger service validates that a user is able to receive challenges at
+an address (such as e-mail or SMS) and allows an OAuth 2.0 client to obtain
+access to these validated addresses.
+
+The high-level flow is that an OAuth 2.0 client is first registered with the
+challenger service (via command-line). Using the command-line tool will print
+the resulting client ID to the console.
+
+ .. note::
+
+ The current service mandates that redirection URIs
+ start with "http://" or "https://". See issue #7838
+ for what should be done to lift this restriction.
+
+ .. note::
+
+ Right now, registration of a unique redirection URI is *mandatory* for
+ each client. If multiple redirection URIs are needed, it is suggested to
+ just register additional clients. (While OAuth 2.0 would support not
+ registering fixed redirection URIs with a client, this is not recommended
+ as it would create an open redirector.)
+
+Once a client is registered, that client can use the challenger service when
+it needs a user to prove that the user is able to receive messages at a
+particular address. However, asking a user to prove access to a particular
+address can be expensive as it may involve sending an SMS or even postal mail
+depending on the type of address. Thus, challenger does not allow a user
+agent to begin an address validation process without prior approval by a
+registered client. Thus, the process begins with a ``/setup/$CLIENT_ID`` request where a
+client requests challenger to begin an address validation request. The
+``/setup/$CLIENT_ID`` response contains a ``nonce`` which is then used to construct the
+URL of the endpoint to which the client must redirect the user-agent to begin
+the address validation and authorization process.
+
+The client then redirects the user-agent to the ``/authorize/$NONCE`` endpoint
+of the challenger service, adding its ``state``, ``client_id`` and
+``redirect_uri`` as query parameters. The ``redirect_uri`` must match the
+redirect URI registered with the client. From this endpoint, the challenger
+service will return a Web page asking the user to provide its address.
+
+ .. note::
+
+ Challenger is a bit unusual in that the ``$NONCE`` in the endpoint URL
+ makes the authorization endpoint URL (deliberately) unpredictable, while
+ for many other OAuth 2.0 APIs this endpoint is static. However, this is
+ compliant with OAuth 2.0 as determining the authorization URL is left out
+ of the scope of the standard.
+
+When the user has filled in the form with their address, it will be submitted
+to the ``/challenge/$NONCE`` endpoint and the challenger service will send a
+challenge to the user's address and generate an HTML form asking the user to
+enter the received challenge value.
+
+The user can then enter the answer to the challenge which is then submitted to
+the ``/solve/$NONCE`` endpoint. If the answer is correct, the user agent will
+be redirected to the client redirect URI that was specified by the OAuth 2.0
+client upon ``/authorize``, together with an authorization grant encoded in
+the redirection URI.
+
+Given this authorization grant, the OAuth 2.0 client can then use the
+``/token`` endpoint to obtain an access token which will grant it access to
+the resource.
+
+Using the ``/info`` endpoint the client can then finally obtain the (now)
+verified address of the user.
+
+.. contents:: Table of Contents
+ :local:
+
+.. include:: tos.rst
+
+-----------------------
+Receiving Configuration
+-----------------------
+
+.. http:get:: /config
+
+ Obtain the key configuration settings of the storage service.
+ This specification corresponds to ``current`` protocol being version **0**.
+
+ **Response:**
+
+ Returns a `ChallengerTermsOfServiceResponse`.
+
+ .. ts:def:: ChallengerTermsOfServiceResponse
+
+ interface ChallengerTermsOfServiceResponse {
+ // Name of the service
+ name: "challenger";
+
+ // libtool-style representation of the Challenger protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v0, may become mandatory in the future.
+ implementation?: string;
+
+ }
+
+.. _challenger-setup:
+
+-----
+Setup
+-----
+
+.. http:post:: /setup/$CLIENT_ID
+
+ This endpoint is used by the client to authorize the execution of an address
+ validation on its behalf. An ``Authorization`` header (for now always using
+ a ``Bearer`` token) should be included to provide the client's credentials
+ to authorize access to the challenger service. This token must match the
+ ``client_secret`` from the registration of the client with the challenger
+ service (which will also be used in the later ``/token`` request).
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `ChallengeSetupResponse`.
+ :http:statuscode:`404 Not found`:
+ The backup service is unaware of a matching client.
+ or the credentials of the client are invalid.
+
+ **Details::**
+
+ .. ts:def:: ChallengeSetupResponse
+
+ interface ChallengeSetupResponse {
+ // Nonce to use when constructing ``/authorize`` endpoint.
+ nonce: string;
+ }
+
+
+.. _challenger-login:
+
+-----
+Login
+-----
+
+.. http:get:: /authorize/$NONCE
+.. http:post:: /authorize/$NONCE
+
+ This is the "authorization" endpoint of the OAuth 2.0 protocol. This
+ endpoint is used by the user-agent. It will return a form to enter the
+ address.
+
+ **Request:**
+
+ :query response_type: Must be ``code``
+ :query client_id: Identifier of the client.
+ :query redirect_uri: URI-encoded redirection URI to use upon authorization.
+ :query state: Arbitrary client state to associate with the request.
+ :query scope: Not supported, any value is accepted.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body contains a form to be submitted by the user-agent.
+ The form will ask the user to specify their address.
+ :http:statuscode:`404 Not found`:
+ The backup service is unaware of a matching $NONCE.
+
+
+.. _challenger-challenge:
+
+---------
+Challenge
+---------
+
+.. http:post:: /challenge/$NONCE
+
+ This endpoint is used by the user-agent to submit the address to which a
+ challenge should be sent by the challenger service.
+
+ **Request:**
+
+ Body should use the mime-type "application/x-www-form-urlencoded".
+ The posted form data must contain an "address".
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body contains a form asking for the answer to
+ the challenge to be entered by the user.
+ :http:statuscode:`404 Not found`:
+ The challenger service is unaware of a matching nonce.
+ :http:statuscode:`429 Too Many Requests`:
+ There have been too many attempts to request challenge
+ transmissions for this $NONCE. The user-agent should
+ wait and (eventually) request a fresh nonce to be set
+ up by the client.
+
+
+.. _challenger-solve:
+
+-----
+Solve
+-----
+
+.. http:post:: /solve/$NONCE
+
+ Used by the user-agent to submit an answer to the challenge. If the answer
+ is correct, the user will be redirected to the client's redirect URI,
+ otherwise the user may be given another chance to complete the process.
+
+ **Request:**
+
+ Depends on the form from ``/challenge``. TBD.
+
+ **Response:**
+
+ :http:statuscode:`302 Found`:
+ The user is redirected to the redirect URI of the client to pass the
+ grant to the client. The target will be the redirect URI specified
+ by the client (during registration and again upon ``/authorize``),
+ plus a ``code`` argument with the authorization code, and the
+ ``state`` argument from the ``/authorize`` endpoint.
+ :http:statuscode:`403 Forbidden`:
+ The solution of the user to the challenge is invalid.
+ :http:statuscode:`404 Not found`:
+ The service is unaware of a matching challenge.
+ :http:statuscode:`429 Too Many Requests`:
+ There have been too many attempts to solve the challenge
+ for this address (and $NONCE). The user-agent should
+ either try a different address (or wait and (eventually)
+ request a fresh nonce to be set up by the client).
+
+
+.. _challenger-auth:
+
+----
+Auth
+----
+
+.. http:post:: /token
+
+ This is the token endpoint of the OAuth 2.0 specification.
+ This endpoint is used by the client to provide its authorization code,
+ demonstrating that it has the right to learn a particular user's validated
+ address. In return, the challenger service returns the access token.
+ Renewal is not supported.
+
+ **Request:**
+
+ The request must include an ``application/www-form-urlencoded`` body
+ specifying the ``client_id``, ``redirect_uri``, ``client_secret``, ``code``
+ and ``grant_type``. The ``grant_type`` must be set to
+ ``authorization_code``. The ``redirect_uri`` must match the URI from
+ ``/authorize``. The ``code`` must be the authorization code that ``/solve``
+ returned to the user. The ``client_id`` and ``client_secret`` must match
+ the usual client credentials.
+
+ **Response:**
+
+ Error responses follow RFC 6749, section 5.2 with an "error" field in JSON,
+ as well as also returning GNU Taler style error messages.
+
+ :http:statuscode:`200 OK`:
+ The body will be a `ChallengerAuthResponse`
+ :http:statuscode:`403 Forbidden`:
+ The credentials of the client are invalid.
+ :http:statuscode:`404 Not found`:
+ The service is unaware of a matching login process.
+
+ **Details::**
+
+ .. ts:def:: ChallengerAuthResponse
+
+ interface ChallengerAuthResponse {
+ // Token used to authenticate access in ``/info``.
+ access_token: string;
+
+ // Type of the access token.
+ token_type: "Bearer";
+
+ // Amount of time that an access token is valid (in seconds).
+ expires_in: Integer;
+
+ }
+
+
+.. _challenger-info:
+
+----
+Info
+----
+
+.. http:get:: /info
+
+ This userinfo endpoint of the OAuth 2.0 specification.
+ This endpoint is used by the client to obtain the user's validated address.
+
+ **Request:**
+
+ Must include the token returned to the client from the ``/token`` endpoint
+ as a ``Bearer`` token in an ``Authorization`` header.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body contains the address as a `ChallengerInfoResponse`.
+ :http:statuscode:`403 Forbidden`:
+ The bearer token is missing or invalid (malformed).
+ :http:statuscode:`404 Not found`:
+ The bearer token is invalid (includes unknown or expired).
+
+ **Details::**
+
+ .. ts:def:: ChallengerInfoResponse
+
+ interface ChallengerInfoResponse {
+
+ // Unique ID of the record within Challenger
+ // (identifies the rowid of the token).
+ id: Integer;
+
+ // Address that was validated.
+ // Key-value pairs, details depend on the
+ // address_type.
+ address: Object;
+
+ // Type of the address.
+ address_type: string;
+
+ // How long do we consider the address to be
+ // valid for this user.
+ expires: Timestamp;
+
+ }
diff --git a/core/api-common.rst b/core/api-common.rst
index ab7726a1..85942a02 100644
--- a/core/api-common.rst
+++ b/core/api-common.rst
@@ -18,10 +18,14 @@
.. _http-common:
-=================================
-Common Taler HTTP API Conventions
-=================================
+.. _foo_bar:
+==================================
+Conventions for Taler RESTful APIs
+==================================
+
+.. contents:: Table of Contents
+ :local:
-------------------------
HTTP Request and Response
@@ -69,6 +73,8 @@ handle the error as if an internal error (500) had been returned.
requests.
:http:statuscode:`400 Bad request`:
One of the arguments to the request is missing or malformed.
+ :http:statuscode:`415 Unsupported Media Type`:
+ The Content-Type header was not set, or it was set to an unsupported MIME type.
:http:statuscode:`500 Internal server error`:
This always indicates some serious internal operational error of the exchange,
such as a program bug, database problems, etc., and must not be used for
@@ -186,6 +192,29 @@ Examples:
to decide whether it will talk to the service.
+.. _error-codes:
+
+-----------
+Error Codes
+-----------
+
+All error codes used in GNU Taler are defined in
+`GANA <https://git.gnunet.org/gana.git/tree/gnu-taler-error-codes/>`__.
+
+This centralized registry also contains generators that create enumerations
+and mappings from error codes to HTTP status codes and human-readable error
+messages for various programming languages.
+
+All error codes have numeric values below 100 or above 1000, so as to never be
+confused with HTTP status codes. A value of 0 is reserved for "no error" or
+"success".
+
+In C, the respective enumeration is the ``enum TALER_ErrorCode``.
+
+Developers may have to re-run ``bootstrap`` and/or update their Git
+submodules to ensure that they have the lastest GANA registry.
+
+
.. _encodings-ref:
----------------
@@ -212,6 +241,7 @@ resulting encoding.
Hash codes
^^^^^^^^^^
+
Hash codes are strings representing base32 encoding of the respective
hashed data. See `base32`_.
@@ -278,6 +308,17 @@ Large numbers
Large numbers such as RSA blinding factors and 256 bit keys, are transmitted
as other binary data in Crockford Base32 encoding.
+Decimal numbers
+^^^^^^^^^^^^^^^
+
+..
+ FIXME: explain the representation with strings.
+
+.. ts:def:: DecimalNumber
+
+ // Number with at most 8 fractional digits.
+ type DecimalNumber = string;
+
Timestamps
^^^^^^^^^^
@@ -313,6 +354,14 @@ Integers
// JavaScript numbers restricted to integers.
type Integer = number;
+Floats
+^^^^^^
+
+.. ts:def:: Float
+
+ // JavaScript numbers.
+ type Float = number;
+
Ages
^^^^
@@ -352,6 +401,76 @@ Objects
// JavaScript objects, no further restrictions.
type Object = object;
+
+Contact details
+^^^^^^^^^^^^^^^
+
+.. ts:def:: EmailAddress
+
+ type EmailAddress = string;
+
+.. ts:def:: PhoneNumber
+
+ type PhoneNumber = string;
+
+Phone numbers should start with the ``+`` symbol and the country code.
+
+Permissions
+^^^^^^^^^^^
+
+This type epresses which permissions for a subject
+apply on a resource.
+
+.. ts:def:: LibeufinPermission
+
+ interface LibeufinPermission {
+ subjectType: string;
+ subjectId: string;
+ resourceType: string;
+ resourceId: string;
+ permissionName: string
+ }
+
+
+Fetch params
+^^^^^^^^^^^^
+
+.. _fetch-params:
+
+.. ts:def:: FetchParams
+
+ interface FetchParams {
+
+ // Because transactions are delivered by banks in "batches",
+ // then every batch can have different qualities. This value
+ // lets the request specify which type of batch ought to be
+ // returned. Currently, the following two type are supported:
+ //
+ // 'report': typically includes only non booked transactions.
+ // 'statement': typically includes only booked transactions.
+ level: "report" | "statement" | "all";
+
+ // This type indicates the time range of the query.
+ // It allows the following values:
+ //
+ // 'latest': retrieves the last transactions from the bank.
+ // If there are older unread transactions, those will *not*
+ // be downloaded.
+ //
+ // 'all': retrieves all the transactions from the bank,
+ // until the oldest.
+ //
+ // 'previous-days': currently *not* implemented, it will allow
+ // the request to download transactions from
+ // today until N days before.
+ //
+ // 'since-last': retrieves all the transactions since the last
+ // time one was downloaded.
+ //
+ rangeType: "latest" | "all" | "previous-days" | "since-last";
+ };
+
+
Keys
^^^^
@@ -434,7 +553,7 @@ Blinded coin
// coin's `public EdDSA key <eddsa-coin-pub>`.
interface RSACoinEnvelope {
cipher: "RSA" | "RSA+age_restricted";
- rsa_blinded_planchet: string; // Crockford `Base32` encoded
+ rsa_blinded_planchet: BlindedRsaSignature;
}
.. ts:def:: CSCoinEnvelope
@@ -458,6 +577,25 @@ Blinded coin
type DenominationBlindingKeyP = string;
+. _unblinded-coin:
+
+Unblinded coin
+^^^^^^^^^^^^^^
+
+.. ts:def:: UnblindedSignature
+
+ // The type of a coin's unblinded signature depends on the cipher that was used
+ // for signing with a denomination key.
+ // Note that for now, only RSA is supported.
+ type UnblindedSignature = RsaUnblindedSignature;
+
+.. ts:def:: RsaUnblindedSignature
+
+ interface RsaUnblindedSignature {
+ cipher: "RSA";
+ rsa_signature: RsaSignature;
+ }
+
.. _signature:
@@ -527,6 +665,12 @@ The following constrains apply for a valid amount:
An amount that is prefixed with a ``+`` or ``-`` character is also used in certain contexts.
When no sign is present, the amount is assumed to be positive.
+.. note::
+
+ In some setups, when Libeufin-Bank offers cashouts towards traditional
+ currencies like EUR for example, the fractional part gets restricted
+ to at most 2 digits.
+
.. ts:def:: SignedAmount
type SignedAmount = string;
@@ -672,6 +816,13 @@ uses 512-bit hash codes (64 bytes).
struct GNUNET_HashCode hash;
};
+.. sourcecode:: c
+
+ struct TALER_OutputCommitmentHash {
+ struct GNUNET_HashCode hash;
+ };
+
+
.. _TALER_EcdhEphemeralPublicKeyP:
.. sourcecode:: c
@@ -823,10 +974,19 @@ uses 512-bit hash codes (64 bytes).
uint8_t enc[sizeof (struct TALER_LinkSecretP)];
};
+.. _eddsa-token-pub:
+.. sourcecode:: c
+
+ union TALER_TokenPublicKeyP {
+ uint8_t eddsa_pub[32];
+ uint8_t ecdhe_pub[32];
+ };
+
.. _Signatures:
Signatures
^^^^^^^^^^
+
Any piece of signed data, complies to the abstract data structure given below.
.. sourcecode:: c
@@ -841,9 +1001,11 @@ Any piece of signed data, complies to the abstract data structure given below.
/*From gnunet_crypto_lib.h*/
struct GNUNET_CRYPTO_EccSignaturePurpose {
/**
-
- The following constraints apply for a valid amount:
-
+ * This field equals the number of bytes being signed,
+ * namely 'sizeof (struct Data)'.
+ */
+ uint32_t size;
+ /**
* This field is used to express the context in
* which the signature is made, ensuring that a
* signature cannot be lifted from one part of the protocol
@@ -851,11 +1013,6 @@ Any piece of signed data, complies to the abstract data structure given below.
* exchange's codebase (git://taler.net/exchange).
*/
uint32_t purpose;
- /**
- * This field equals the number of bytes being signed,
- * namely 'sizeof (struct Data)'.
- */
- uint32_t size;
};
@@ -888,13 +1045,31 @@ within the
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_ReservePublicKeyP reserve_pub;
- struct GNUNET_HashCode age_restricted_coins_commitment;
- struct GNUNET_HashCode h_denoms_h;
- uint8 max_age;
+ /**
+ * This is the running SHA512-hash over n*kappa
+ * `struct TALER_BlindedCoinHash` values
+ */
+ struct GNUNET_HashCode h_commitment;
+ struct TALER_AgeMask mask;
+ uint8_t max_age_group;
+ };
+
+.. _TALER_AgeWithdrawConfirmationPS:
+
+.. sourcecode:: c
+
+ struct TALER_AgeWithdrawConfirmationPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode h_commitment;
+ uint32_t noreveal_index;
};
-.. _TALER_DepositRequestPS:
+.. _taler_depositrequestps:
+
.. sourcecode:: c
struct TALER_DepositRequestPS {
@@ -912,9 +1087,15 @@ within the
struct TALER_AmountNBO amount_with_fee;
struct TALER_AmountNBO deposit_fee;
struct TALER_MerchantPublicKeyP merchant;
+ struct GNUNET_HashCode wallet_data_hash;
+ /* @since protocol **vSUBSCRIBE** */
+ struct TALER_OutputCommitmentHash h_outputs;
+ /* @since protocol **vSUBSCRIBE** */
+ uint16_t choice_index;
};
.. _TALER_DepositConfirmationPS:
+
.. sourcecode:: c
struct TALER_DepositConfirmationPS {
@@ -967,7 +1148,6 @@ within the
* purpose.purpose = TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
- struct TALER_MasterPublicKeyP master_public_key;
struct GNUNET_TIME_AbsoluteNBO start;
struct GNUNET_TIME_AbsoluteNBO expire;
struct GNUNET_TIME_AbsoluteNBO end;
@@ -1015,6 +1195,9 @@ within the
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_PaytoHash h_wire_details;
+ struct GNUNET_HashCode h_conversion_url;
+ struct GNUNET_HashCode h_credit_restrictions;
+ struct GNUNET_HashCode h_debit_restrictions;
};
.. _TALER_MasterWireFeePS:
@@ -1035,7 +1218,7 @@ within the
.. _TALER_GlobalFeesPS:
.. sourcecode:: c
- struct TALER_MasterWireFeePS {
+ struct TALER_GlobalFeesPS {
/**
* purpose.purpose = TALER_SIGNATURE_MASTER_GLOBAL_FEES
*/
@@ -1147,10 +1330,7 @@ within the
* purpose.purpose = TALER_SIGNATURE_MERCHANT_CONTRACT
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
- struct TALER_AmountNBO total_amount;
- struct TALER_AmountNBO max_fee;
struct TALER_PrivateContractHash h_contract_terms;
- struct TALER_MerchantPublicKeyP merchant_pub;
};
.. _TALER_ConfirmWirePS:
@@ -1683,6 +1863,9 @@ within the
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct GNUNET_TIME_AbsoluteNBO start_date;
struct TALER_PaytoHash h_wire;
+ struct GNUNET_HashCode h_conversion_url;
+ struct GNUNET_HashCode h_credit_restrictions;
+ struct GNUNET_HashCode h_debit_restrictions;
};
.. _TALER_MasterDelWirePS:
@@ -1712,6 +1895,24 @@ within the
uint32_t is_active GNUNET_PACKED;
};
+. _TALER_AmlDecisionPS:
+.. sourcecode:: c
+
+ struct TALER_AmlDecisionPS
+ {
+ /**
+ * purpose.purpose =TALER_SIGNATURE_AML_DECISION.
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_HashCode h_justification GNUNET_PACKED;
+ struct GNUNET_TIME_TimestampNBO decision_time;
+ struct TALER_AmountNBO new_threshold;
+ struct TALER_PaytoHashP h_payto GNUNET_PACKED;
+ struct GNUNET_HashCode h_kyc_requirements;
+ uint32_t new_state GNUNET_PACKED;
+
+ };
+
.. _TALER_PartnerConfigurationPS:
.. sourcecode:: c
@@ -1778,4 +1979,4 @@ within the
struct GNUNET_TIME_TimestampNBO expiration_time;
struct TALER_ReservePublicKeyP reserve_pub;
struct GNUNET_HashCode h_attributes;
- };
+ };
diff --git a/core/api-corebank.rst b/core/api-corebank.rst
new file mode 100644
index 00000000..ec1d23d2
--- /dev/null
+++ b/core/api-corebank.rst
@@ -0,0 +1,1234 @@
+..
+ This file is part of GNU TALER.
+
+ Copyright (C) 2014-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+.. target audience: developer, core developer
+
+.. _corebank-api:
+
+====================
+Taler Core Bank API
+====================
+
+.. contents:: Table of Contents
+ :local:
+
+Introduction
+------------
+
+The Libeufin bank provides a minimal core banking system. In addition to that,
+it provides features for local/regional currencies.
+
+Authentication
+--------------
+
+Some requests require the client to authenticate via HTTP Basic auth (RFC 7617)
+or using a bearer token which can be obtained or refreshed from the
+``/accounts/$USERNAME/token`` endpoint.
+When using Basic authentication, the user-id must be the bank
+username, and the password the password for the corresponding user.
+
+Another way to obtain a login token is by manually configuring it for certain
+endpoints. For example, the exchange could give an auditor read-only access to
+the taler-wire-gateway facade via such a manually configured access token.
+
+The ``admin`` user is a special, hard-coded username. Some requests require the
+client to authenticate as the admin.
+
+.. http:post:: /accounts/$USERNAME/token
+
+ See :ref:`DD 48 token endpoint <dd48-token>`.
+
+
+Bank Web UI
+-----------
+
+The web UI for the bank is typically served under ``/``.
+
+Config
+------
+
+.. http:get:: /config
+
+ Return the protocol version and configuration information about the bank.
+ This specification corresponds to ``current`` protocol being version **4**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `Config`.
+
+ **Details:**
+
+ .. ts:def:: Config
+
+ interface Config {
+ // Name of the API.
+ name: "taler-corebank";
+
+ // libtool-style representation of the Bank protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Bank display name to be used in user interfaces.
+ // For consistency use "Taler Bank" if missing.
+ // @since v4, will become mandatory in the next version.
+ bank_name?: string;
+
+ // If 'true' the server provides local currency conversion support
+ // If 'false' some parts of the API are not supported and return 501
+ allow_conversion: boolean;
+
+ // If 'true' anyone can register
+ // If 'false' only admin can
+ allow_registrations: boolean;
+
+ // If 'true' account can delete themselves
+ // If 'false' only admin can delete accounts
+ allow_deletions: boolean;
+
+ // If 'true' anyone can edit their name
+ // If 'false' only admin can
+ allow_edit_name: boolean;
+
+ // If 'true' anyone can edit their cashout account
+ // If 'false' only admin can
+ allow_edit_cashout_payto_uri: boolean;
+
+ // Default debt limit for newly created accounts
+ default_debit_threshold: Amount;
+
+ // Currency used by this bank.
+ currency: string;
+
+ // How the bank SPA should render this currency.
+ currency_specification: CurrencySpecification;
+
+ // TAN channels supported by the server
+ supported_tan_channels: TanChannel[];
+
+ // Wire transfer type supported by the bank.
+ // Default to 'iban' is missing
+ // @since v4, will become mandatory in the next version.
+ wire_type?: string;
+ }
+
+
+Account Management
+------------------
+
+.. _bank-account-register:
+
+.. http:post:: /accounts
+
+ Create a new bank account. Depending on the configuration,
+ the account creation is self-serve, or only restricted to
+ the administrators.
+
+ **Request:**
+
+ .. ts:def:: RegisterAccountRequest
+
+ interface RegisterAccountRequest {
+ // Username of the account
+ username: string;
+
+ // Password of the account used for authentication
+ password: string;
+
+ // Legal name of the account owner
+ name: string;
+
+ // Make this account visible to anyone?
+ // Defaults to false.
+ is_public?: boolean;
+
+ // Make this account a taler exchange account?
+ // If true:
+ // - incoming transactions to the account that do not
+ // have a valid reserve public key are automatically
+ // - the account provides the taler-wire-gateway-api endpoints
+ // Defaults to false.
+ is_taler_exchange?: boolean;
+
+ // Addresses where to send the TAN for protected operations.
+ contact_data?: ChallengeContactData;
+
+ // IBAN 'payto' URI of a fiat bank account with a 'receiver-name' parameter.
+ // If 'receiver-name' is missing, ``name`` will be used instead.
+ // Payments will be sent to this bank account
+ // when the user wants to convert the regional currency
+ // back to fiat currency outside bank.
+ cashout_payto_uri?: string;
+
+ // Internal payto URI of this bank account.
+ // Used mostly for testing, this field is ignored if the bank payment
+ // method is not IBAN.
+ payto_uri?: string;
+
+ // If present, set the max debit allowed for this user
+ // Only admin can set this property.
+ debit_threshold?: Amount;
+
+ // If present, enables 2FA and set the TAN channel used for challenges
+ // Only admin can set this property, other user can reconfig their account
+ // after creation.
+ tan_channel?: TanChannel;
+ }
+
+ .. ts:def:: ChallengeContactData
+
+ interface ChallengeContactData {
+ // E-Mail address
+ email?: EmailAddress;
+
+ // Phone number.
+ phone?: PhoneNumber;
+ }
+
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `RegisterAccountResponse`.
+ :http:statuscode:`400 Bad request`:
+ Input data was invalid. For example, the client specified a invalid
+ phone number or e-mail address.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_REGISTER_USERNAME_REUSE`` : username already used.
+ * ``TALER_EC_BANK_REGISTER_PAYTO_URI_REUSE`` : payto URI already used.
+ * ``TALER_EC_BANK_UNALLOWED_DEBIT`` : admin account does not have sufficient funds to grant bonus.
+ * ``TALER_EC_BANK_RESERVED_USERNAME_CONFLICT`` : a reserved username was attempted, like ``admin`` or ``bank``
+ * ``TALER_EC_BANK_NON_ADMIN_PATCH_DEBT_LIMIT`` : a non-admin user has tried to create an account with a customer debt limit.
+ * ``TALER_EC_BANK_NON_ADMIN_SET_TAN_CHANNEL`` : a non-admin user has tried to create an account with 2fa.
+ * ``TALER_EC_BANK_TAN_CHANNEL_NOT_SUPPORTED``: ``tan_channel`` is not supported, check bank config to find supported ones.
+ * ``TALER_EC_BANK_MISSING_TAN_INFO``: the user did not share any contact data where to send the TAN via ``tan_channel``.
+
+ **Details:**
+
+ .. ts:def:: RegisterAccountResponse
+
+ interface RegisterAccountResponse {
+ // Internal payto URI of this bank account.
+ internal_payto_uri: string;
+ }
+
+.. _delete-account:
+
+.. http:delete:: /accounts/$USERNAME
+
+ Delete the account whose username is ``$USERNAME``. The deletion
+ succeeds only if the balance is *zero*. Typically only available to
+ the administrator, but can be configured to allow ordinary users too.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`204 No content`:
+ The account was successfully deleted.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_RESERVED_USERNAME_CONFLICT`` : a reserved username was attempted, like ``admin`` or ``bank``.
+ * ``TALER_EC_BANK_ACCOUNT_BALANCE_NOT_ZERO``: the account balance was not zero.
+
+.. _account-reconfig:
+
+.. http:patch:: /accounts/$USERNAME
+
+ Allows reconfiguring the account data of ``$USERNAME``.
+
+ **Request:**
+
+ .. ts:def:: AccountReconfiguration
+
+ interface AccountReconfiguration {
+ // Addresses where to send the TAN for protected operations.
+ contact_data?: ChallengeContactData;
+
+ // IBAN 'payto' URI of a fiat bank account with a 'receiver-name' parameter.
+ // If 'receiver-name' is missing, ``name`` will be used instead.
+ // Payments will be sent to this bank account
+ // when the user wants to convert the regional currency
+ // back to fiat currency outside bank.
+ // Only admin can change this property if not allowed in config
+ cashout_payto_uri?: string;
+
+ // If present, change the legal name associated with $username.
+ // Only admin can change this property if not allowed in config
+ name?: string;
+
+ // Make this account visible to anyone?
+ is_public?: boolean;
+
+ // If present, change the max debit allowed for this user
+ // Only admin can change this property.
+ debit_threshold?: Amount;
+
+ // If present, enables 2FA and set the TAN channel used for challenges
+ tan_channel?: TanChannel;
+ }
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`204 No content`:
+ Operation successful.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_NON_ADMIN_PATCH_LEGAL_NAME`` : a non-admin user has tried to change their legal name.
+ * ``TALER_EC_BANK_NON_ADMIN_PATCH_CASHOUT`` : a non-admin user has tried to change their cashout account.
+ * ``TALER_EC_BANK_NON_ADMIN_PATCH_DEBT_LIMIT`` : a non-admin user has tried to change their debt limit.
+ * ``TALER_EC_BANK_TAN_CHANNEL_NOT_SUPPORTED`` : ``tan_channel`` is not supported, check bank config to find supported ones.
+ * ``TALER_EC_BANK_MISSING_TAN_INFO`` : the user did not share any contact data where to send the TAN via ``tan_channel``.
+
+
+.. _account-password-reconfig:
+
+.. http:patch:: /accounts/$USERNAME/auth
+
+ Allows changing the account's password.
+
+
+ **Request:**
+
+ .. ts:def:: AccountPasswordChange
+
+ interface AccountPasswordChange {
+ // Old password. If present it need to match the current
+ // password before updating.
+ old_password?: string;
+ // New password.
+ new_password: string;
+ }
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`204 No content`:
+ Operation successful.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD``: a non-admin user has tried to change their password whihout providing the current one.
+ * ``TALER_EC_BANK_PATCH_BAD_OLD_PASSWORD`` : provided old password does not match current password.
+
+.. _account-list:
+
+.. http:get:: /public-accounts
+
+ Show those accounts whose histories are publicly visible. For example,
+ accounts from donation receivers. As such, this request is unauthenticated.
+
+
+ **Request:**
+
+ :query delta: *Optional.*
+ Takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries.
+ :query start: *Optional.*
+ Row number threshold, see ``delta`` for its interpretation. Defaults to smallest or biggest row id possible according to ``delta`` sign.
+ :query filter_name: *Optional.*
+ Pattern to filter on the account legal name. Given
+ the filter 'foo', all the results will **contain**
+ 'foo' in their legal name. Without this option,
+ all the existing accounts are returned.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `PublicAccountsResponse`.
+ :http:statuscode:`204 No content`:
+ No public account.
+
+ **Details:**
+
+ .. ts:def:: PublicAccountsResponse
+
+ interface PublicAccountsResponse {
+ public_accounts: PublicAccount[];
+ }
+
+ .. ts:def:: PublicAccount
+
+ interface PublicAccount {
+ // Username of the account
+ username: string;
+
+ // Internal payto URI of this bank account.
+ payto_uri: string;
+
+ // Current balance of the account
+ balance: Balance;
+
+ // Is this a taler exchange account?
+ is_taler_exchange: boolean;
+
+ // Opaque unique ID used for pagination.
+ // @since v4, will become mandatory in the next version.
+ row_id?: Integer;
+ }
+
+.. http:get:: /accounts
+
+ Obtains a list of the accounts registered at the bank.
+ It returns only the information that this API handles, without
+ any balance or transactions list.
+ This request is only available to the administrator.
+
+ **Request:**
+
+ :query delta: *Optional.*
+ Takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries.
+ :query start: *Optional.*
+ Row number threshold, see ``delta`` for its interpretation. Defaults to smallest or biggest row id possible according to ``delta`` sign.
+ :query filter_name: *Optional.*
+ Pattern to filter on the account legal name. Given
+ the filter 'foo', all the results will **contain**
+ 'foo' in their legal name. Without this option,
+ all the existing accounts are returned.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ At least one account was found.
+ The server responds with a `ListBankAccountsResponse` object.
+ :http:statuscode:`204 No Content`:
+ No accounts were found for the given request.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+
+ **Details:**
+
+ .. ts:def:: ListBankAccountsResponse
+
+ interfaces ListBankAccountsResponse {
+ accounts: AccountMinimalData[];
+ }
+
+ .. ts:def:: Balance
+
+ interface Balance {
+ amount: Amount;
+ credit_debit_indicator: "credit" | "debit";
+ }
+
+ .. ts:def:: AccountMinimalData
+
+ interface AccountMinimalData {
+ // Username of the account
+ username: string;
+
+ // Legal name of the account owner.
+ name: string;
+
+ // Internal payto URI of this bank account.
+ payto_uri: string;
+
+ // Current balance of the account
+ balance: Balance;
+
+ // Number indicating the max debit allowed for the requesting user.
+ debit_threshold: Amount;
+
+ // Is this account visible to anyone?
+ is_public: boolean;
+
+ // Is this a taler exchange account?
+ is_taler_exchange: boolean;
+
+ // Opaque unique ID used for pagination.
+ // @since v4, will become mandatory in the next version.
+ row_id?: Integer;
+
+ // Current status of the account
+ // active: the account can be used
+ // deleted: the account has been deleted but is retained for compliance
+ // reasons, only the administrator can access it
+ // Default to 'active' is missing
+ // @since v4, will become mandatory in the next version.
+ status?: "active" | "deleted";
+ }
+
+.. _bank-account-info:
+
+.. http:get:: /accounts/$USERNAME
+
+ Obtains information relative to the account owned by
+ ``$USERNAME``. The request is available to the administrator
+ and ``$USERNAME`` itself.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The bank responds with an `AccountData` object.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+
+ **Details:**
+
+ .. ts:def:: AccountData
+
+ interface AccountData {
+ // Legal name of the account owner.
+ name: string;
+
+ // Available balance on the account.
+ balance: Balance;
+
+ // payto://-URI of the account.
+ payto_uri: string;
+
+ // Number indicating the max debit allowed for the requesting user.
+ debit_threshold: Amount;
+
+ // Addresses where to send the TAN for transactions.
+ // Currently only used for cashouts.
+ // If missing, cashouts will fail.
+ // In the future, might be used for other transactions
+ // as well.
+ contact_data?: ChallengeContactData;
+
+ // IBAN 'payto' URI with a 'receiver-name' parameter of a fiat bank
+ // account where to send cashouts. This field is optional
+ // because not all the accounts are required to participate
+ // in the merchants' circuit. One example is the exchange:
+ // that never cashouts. Registering these accounts can
+ // be done via the access API.
+ cashout_payto_uri?: string;
+
+ // Is this account visible to anyone?
+ is_public: boolean;
+
+ // Is this a taler exchange account?
+ is_taler_exchange: boolean;
+
+ // Is 2FA enabled and what channel is used for challenges?
+ tan_channel?: TanChannel;
+
+ // Current status of the account
+ // active: the account can be used
+ // deleted: the account has been deleted but is retained for compliance
+ // reasons, only the administrator can access it
+ // Default to 'active' is missing
+ // @since v4, will become mandatory in the next version.
+ status?: "active" | "deleted";
+ }
+
+Transactions
+------------
+
+.. http:get:: /accounts/$USERNAME/transactions
+
+ Retrieve a subset of transactions related to $USERNAME.
+
+ The list of returned transactions is determined by a row ID *starting point*
+ and a signed non-zero integer *delta*:
+
+ * If *delta* is positive, return a list of up to *delta* transactions (all matching
+ the filter criteria) strictly **after** the starting point. The transactions are sorted
+ in **ascending** order of the row ID.
+ * If *delta* is negative, return a list of up to *-delta* transactions (all matching
+ the filter criteria) strictly **before** the starting point. The transactions are sorted
+ in **descending** order of the row ID.
+
+ If *starting point* is not explicitly given, it defaults to:
+
+ * A value that is **smaller** than all other row IDs if *delta* is **positive**.
+ * A value that is **larger** than all other row IDs if *delta* is **negative**.
+
+ **Request:**
+
+ :query delta: *Optional.*
+ Takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries.
+ :query start: *Optional.*
+ Row number threshold, see ``delta`` for its interpretation. Defaults to smallest or biggest row id possible according to ``delta`` sign.
+ :query long_poll_ms: Optional number to express how many milliseconds the server
+ should wait for at least one result to be shown. If not given, the server
+ responds immediately, regardless of the result.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The bank responds with an `BankAccountTransactionsResponse` object.
+ :http:statuscode:`204 No content`:
+ No transaction found.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+
+ **Details:**
+
+ .. ts:def:: BankAccountTransactionsResponse
+
+ interface BankAccountTransactionsResponse {
+ transactions: BankAccountTransactionInfo[];
+ }
+
+.. http:get:: /accounts/$USERNAME/transactions/$TRANSACTION_ID
+
+ Retrieve the transaction whose identifier is ``TRANSACTION_ID``.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The bank responds with an `BankAccountTransactionInfo` object.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+
+ **Details:**
+
+ .. ts:def:: BankAccountTransactionInfo
+
+ interface BankAccountTransactionInfo {
+ creditor_payto_uri: string;
+ debtor_payto_uri: string;
+
+ amount: Amount;
+ direction: "debit" | "credit";
+
+ subject: string;
+
+ // Transaction unique ID. Matches
+ // $TRANSACTION_ID from the URI.
+ row_id: Integer;
+ date: Timestamp;
+ }
+
+.. http:post:: /accounts/$USERNAME/transactions
+
+ Create a new transaction where the bank account with the label ``USERNAME`` is **debited**.
+
+ **Request:**
+
+ .. ts:def:: CreateTransactionRequest
+
+ interface CreateTransactionRequest {
+ // Address in the Payto format of the wire transfer receiver.
+ // It needs at least the 'message' query string parameter.
+ payto_uri: string;
+
+ // Transaction amount (in the $currency:x.y format), optional.
+ // However, when not given, its value must occupy the 'amount'
+ // query string parameter of the 'payto' field. In case it
+ // is given in both places, the payto_uri's takes the precedence.
+ amount: string;
+
+ // Nonce to make the request idempotent. Requests with the same
+ // ``request_uid`` that differ in any of the other fields
+ // are rejected.
+ // @since v4, will become mandatory in the next version.
+ request_uid?: ShortHashCode;
+ }
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The bank responds with an `CreateTransactionResponse` object.
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`400 Bad Request`:
+ The request was invalid or the payto://-URI used unacceptable features.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_SAME_ACCOUNT`` : creditor account is the same than ``USERNAME``.
+ * ``TALER_EC_BANK_UNKNOWN_CREDITOR`` : creditor account was not found.
+ * ``TALER_EC_BANK_UNALLOWED_DEBIT`` : the account does not have sufficient funds.
+ * ``TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED``: an operation with the same ``request_uid`` but different details has been submitted before.
+
+ **Details:**
+
+ .. ts:def:: CreateTransactionResponse
+
+ interface CreateTransactionResponse {
+ // ID identifying the transaction being created
+ row_id: Integer;
+ }
+
+Taler Withdrawals
+-----------------
+
+.. http:post:: /accounts/$USERNAME/withdrawals
+
+ Create a withdrawal operation, resulting in a ``taler://withdraw`` URI.
+
+ **Request:**
+
+ .. ts:def:: BankAccountCreateWithdrawalRequest
+
+ interface BankAccountCreateWithdrawalRequest {
+ // Amount to withdraw.
+ amount: Amount;
+ }
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The bank responds with an `BankAccountCreateWithdrawalResponse` object.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`409 Conflict`:
+ The account does not have sufficient funds.
+
+ **Details:**
+
+ .. ts:def:: BankAccountCreateWithdrawalResponse
+
+ interface BankAccountCreateWithdrawalResponse {
+ // ID identifying the operation being created
+ withdrawal_id: string;
+
+ // URI that can be passed to the wallet to initiate the withdrawal
+ taler_withdraw_uri: string;
+ }
+
+.. http:post:: /accounts/$USERNAME/withdrawals/$WITHDRAWAL_ID/confirm
+
+ Confirms ``WITHDRAWAL_ID`` operation. Has no effect on an already confirmed
+ withdrawal operation. This call is responsible for wiring the funds to the
+ exchange.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`204 No content`:
+ The withdrawal operation has been confirmed.
+ :http:statuscode:`404 Not found`:
+ The operation was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_CONFIRM_ABORT_CONFLICT`` : the withdrawal has been aborted previously and can't be confirmed.
+ * ``TALER_EC_BANK_CONFIRM_INCOMPLETE`` : the withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before.
+ * ``TALER_EC_BANK_UNALLOWED_DEBIT`` : the account does not have sufficient funds.
+
+.. http:post:: /accounts/$USERNAME/withdrawals/$WITHDRAWAL_ID/abort
+
+ Aborts ``WITHDRAWAL_ID`` operation. Has no effect on an already aborted
+ operation.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The withdrawal operation has been aborted.
+ :http:statuscode:`404 Not found`:
+ The withdrawal operation was not found.
+ :http:statuscode:`409 Conflict`:
+ The withdrawal operation has been confirmed previously and can't be aborted.
+
+.. http:get:: /withdrawals/$WITHDRAWAL_ID
+
+ Retrieve public information about ``WITHDRAWAL_ID`` withdrawal operation.
+ Does not require further authentication as knowledge of ``WITHDRAWAL_ID``
+ serves as an authenticator.
+
+ **Request:**
+
+ :query long_poll_ms:
+ *Optional.* If specified, the bank will wait up to ``long_poll_ms``
+ milliseconds for operationt state to be different from ``old_state`` before sending the HTTP
+ response. A client must never rely on this behavior, as the bank may
+ return a response immediately.
+ :query old_state:
+ *Optional.* Default to "pending".
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The bank responds with an `WithdrawalPublicInfo` object.
+ :http:statuscode:`404 Not found`:
+ The operation was not found.
+
+ **Details:**
+
+ .. ts:def:: WithdrawalPublicInfo
+
+ interface WithdrawalPublicInfo {
+ // Current status of the operation
+ // pending: the operation is pending parameters selection (exchange and reserve public key)
+ // selected: the operations has been selected and is pending confirmation
+ // aborted: the operation has been aborted
+ // confirmed: the transfer has been confirmed and registered by the bank
+ status: "pending" | "selected" | "aborted" | "confirmed";
+
+ // Amount that will be withdrawn with this operation
+ // (raw amount without fee considerations).
+ amount: Amount;
+
+ // Account username
+ username: string;
+
+ // Reserve public key selected by the exchange,
+ // only non-null if ``status`` is ``selected`` or ``confirmed``.
+ selected_reserve_pub?: string;
+
+ // Exchange account selected by the wallet
+ // only non-null if ``status`` is ``selected`` or ``confirmed``.
+ selected_exchange_account?: string;
+ }
+
+Cashouts
+--------
+
+.. _account-cashout:
+
+.. http:post:: /accounts/$USERNAME/cashouts
+
+ Initiates a conversion to fiat currency. The fiat
+ bank account to be
+ credited is the one specified at registration time via the
+ *cashout_payto_uri* parameter. The regional bank account
+ is specified via ``$USERNAME``.
+
+ .. note::
+
+ Consult the `cashout rates call <cashout-rates_>`_ to learn
+ about any applicable fee or exchange rate.
+
+
+ **Request:**
+
+ .. ts:def:: CashoutRequest
+
+ interface CashoutRequest {
+ // Nonce to make the request idempotent. Requests with the same
+ // ``request_uid`` that differ in any of the other fields
+ // are rejected.
+ request_uid: ShortHashCode;
+
+ // Optional subject to associate to the
+ // cashout operation. This data will appear
+ // as the incoming wire transfer subject in
+ // the user's fiat bank account.
+ subject?: string;
+
+ // That is the plain amount that the user specified
+ // to cashout. Its $currency is the (regional) currency of the
+ // bank instance.
+ amount_debit: Amount;
+
+ // That is the amount that will effectively be
+ // transferred by the bank to the user's fiat bank
+ // account.
+ // It is expressed in the fiat currency and
+ // is calculated after the cashout fee and the
+ // exchange rate. See the /cashout-rate call.
+ // The client needs to calculate this amount
+ // correctly based on the amount_debit and the cashout rate,
+ // otherwise the request will fail.
+ amount_credit: Amount;
+ }
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The cashout request was correctly created.
+ This returns the `CashoutResponse` response.
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED``: an operation with the same ``request_uid`` but different details has been submitted before.
+ * ``TALER_EC_BANK_BAD_CONVERSION`` : exchange rate was calculated incorrectly by the client.
+ * ``TALER_EC_BANK_UNALLOWED_DEBIT`` : the account does not have sufficient funds.
+ * ``TALER_EC_BANK_CONFIRM_INCOMPLETE`` : the user did not share any cashout payto to uri where to wire funds.
+ :http:statuscode:`501 Not Implemented`:
+ * ``TALER_EC_BANK_TAN_CHANNEL_NOT_SUPPORTED``: the chosen ``tan_channel`` is not currently supported.
+ * This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: CashoutResponse
+
+ interface CashoutResponse {
+ // ID identifying the operation being created
+ cashout_id: Integer;
+ }
+
+.. _circuit-cashout-details:
+
+.. http:get:: /accounts/$USERNAME/cashouts/$CASHOUT_ID
+
+ Returns information about the status of the ``$CASHOUT_ID`` operation.
+ The request is available to the administrator and the account owner.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `CashoutStatusResponse`.
+ :http:statuscode:`404 Not found`:
+ The cashout operation was not found.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: CashoutStatusResponse
+
+ interface CashoutStatusResponse {
+ // Amount debited to the regional bank account.
+ amount_debit: Amount;
+
+ // Amount credited to the fiat bank account.
+ amount_credit: Amount;
+
+ // Transaction subject.
+ subject: string;
+
+ // Time when the cashout was created.
+ creation_time: Timestamp;
+ }
+
+.. _circuit-cashouts:
+
+.. http:get:: /accounts/$USERNAME/cashouts
+
+ Returns the list of all cash-out operations for an account.
+
+ **Request:**
+
+ :query delta: *Optional.*
+ Takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries.
+ :query start: *Optional.*
+ Row number threshold, see ``delta`` for its interpretation. Defaults to smallest or biggest row id possible according to ``delta`` sign.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `Cashouts`.
+ :http:statuscode:`204 No Content`:
+ No cash-out operations were found.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: Cashouts
+
+ interface Cashouts {
+ // Every string represents a cash-out operation ID.
+ cashouts: CashoutInfo[];
+ }
+
+ .. ts:def:: CashoutInfo
+
+ interface CashoutInfo {
+ cashout_id: Integer;
+ }
+
+.. http:get:: /cashouts
+
+ Returns the list of all cash-out operations for **all** accounts.
+
+ Can only be used by the administrators.
+
+ **Request:**
+
+ :query delta: *Optional.*
+ Takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries.
+ :query start: *Optional.*
+ Row number threshold, see ``delta`` for its interpretation. Defaults to smallest or biggest row id possible according to ``delta`` sign.
+
+ .. note::
+
+ We might want to add a filter in the future to only
+ query pending cashout operations.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `GlobalCashouts`.
+ :http:statuscode:`204 No Content`:
+ No cash-out operations were found.
+ :http:statuscode:`501 Not implemented`:
+ This server does not support conversion, client should check config response.
+
+ **Details:**
+
+ .. ts:def:: GlobalCashouts
+
+ interface GlobalCashouts {
+ cashouts: GlobalCashoutInfo[];
+ }
+
+ .. ts:def:: GlobalCashoutInfo
+
+ interface GlobalCashoutInfo {
+ cashout_id: Integer;
+ username: string;
+ }
+
+.. _cashout-rates:
+
+2FA
+---
+
+.. http:post:: /accounts/$USERNAME/challenge/$CHALLENGE_ID
+
+ Send TAN code for the ``CHALLENGE_ID`` challenge.
+
+ This request can be posted several times to trigger TAN retransmission when the current code has expired or too many confirmation attempts have been made.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The TAN code have been sent. This returns `TanTransmission` response.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not Found`:
+ The challenge was not found.
+ :http:statuscode:`502 Bad Gateway`:
+ * ``TALER_EC_BANK_TAN_CHANNEL_SCRIPT_FAILED``: TAN transmition via ``tan_channel`` failed.
+
+ **Details:**
+
+ .. ts:def:: TanTransmission
+
+ interface TanTransmission {
+ // Channel of the last successful transmission of the TAN challenge.
+ tan_channel: TanChannel;
+
+ // Info of the last successful transmission of the TAN challenge.
+ tan_info: string;
+ }
+
+ .. ts:def:: Challenge
+
+ interface Challenge {
+ // Unique identifier of the challenge to solve to run this protected
+ // operation.
+ challenge_id: string;
+ }
+
+ .. ts:def:: TanChannel
+
+ enum TanChannel {
+ SMS = "sms",
+ EMAIL = "email"
+ }
+
+
+.. http:post:: /accounts/$USERNAME/challenge/$CHALLENGE_ID/confirm
+
+ Solves the ``CHALLENGE_ID`` challenge and allows performing the protected operation.
+
+ When the challenge is confirmed, you can call the protected endpoint again with ``CHALLENGE_ID`` in the ``X-Challenge-Id`` HTTP header and an empty request body.
+
+ **Request:**
+
+ .. ts:def:: ChallengeSolve
+
+ interface ChallengeSolve {
+ // The TAN code that solves $CHALLENGE_ID
+ tan: string;
+ }
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The challenge is confirmed.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not Found`:
+ The challenge was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_TAN_CHALLENGE_FAILED`` : wrong TAN.
+ * ``TALER_EC_BANK_TAN_CHALLENGE_EXPIRED`` : expired TAN.
+ :http:statuscode:`429 Too many requests`:
+ Too many failed confirmation attempts, a new TAN must be requested.
+
+
+Monitor
+-------
+
+.. http:get:: /monitor
+
+ When the bank provides conversion between the local currency and an
+ external one, this call lets the bank administrator monitor the cashin
+ and cashout operations that were made from and to the external currency.
+ It shows as well figures related to internal payments made by a Taler
+ exchange component to internal bank accounts. Timeframes are in UTC.
+
+ **Request:**
+
+ :query timeframe: *Optional*.
+ This parameter admits one of the following values. Defaults to 'hour'.
+
+ * hour
+ * day
+ * month
+ * year
+
+ :query which: *Optional*.
+ This parameter points at a particular element of the *timeframe* parameter.
+ Following are the admitted values for each one.
+ Default to the last snapshot taken of the *timeframe* parameter.
+
+ * hour: from 00 to 23
+ * day: from 1 to the last day of the current month.
+ * month: from 1 to 12
+ * year: Gregorian year in the YYYY format.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The bank responds with `MonitorResponse`.
+ :http:statuscode:`400 Bad Request`:
+ This error may indicate that the *which* parameter is not appropriate for the selected *timeframe*. For example, timeframe=month and which=20 would result in this error.
+
+ **Details:**
+
+ .. note::
+
+ API consumers may combine the values in the response with other
+ factors to serve different views to their users.
+
+ .. ts:def:: MonitorResponse
+
+ // Union discriminated by the "type" field.
+ type MonitorResponse =
+ | MonitorNoConversion
+ | MonitorWithConversion;
+
+ .. ts:def:: MonitorNoConversion
+
+ // Monitoring stats when conversion is not supported
+ interface MonitorNoConversion {
+ type: "no-conversions";
+
+ // How many payments were made to a Taler exchange by another
+ // bank account.
+ talerInCount: Integer;
+
+ // Overall volume that has been paid to a Taler
+ // exchange by another bank account.
+ talerInVolume: Amount;
+
+ // How many payments were made by a Taler exchange to another
+ // bank account.
+ talerOutCount: Integer;
+
+ // Overall volume that has been paid by a Taler
+ // exchange to another bank account.
+ talerOutVolume: Amount;
+ }
+
+ .. ts:def:: MonitorWithConversion
+
+ // Monitoring stats when conversion is supported
+ interface MonitorWithConversion {
+ type: "with-conversions";
+
+ // How many cashin operations were confirmed by a
+ // wallet owner. Note: wallet owners
+ // are NOT required to be customers of the libeufin-bank.
+ cashinCount: Integer;
+
+ // Overall regional currency that has been paid by the regional admin account
+ // to regional bank accounts to fulfill all the confirmed cashin operations.
+ cashinRegionalVolume: Amount;
+
+ // Overall fiat currency that has been paid to the fiat admin account
+ // by fiat bank accounts to fulfill all the confirmed cashin operations.
+ cashinFiatVolume: Amount;
+
+ // How many cashout operations were confirmed.
+ cashoutCount: Integer;
+
+ // Overall regional currency that has been paid to the regional admin account
+ // by fiat bank accounts to fulfill all the confirmed cashout operations.
+ cashoutRegionalVolume: Amount;
+
+ // Overall fiat currency that has been paid by the fiat admin account
+ // to fiat bank accounts to fulfill all the confirmed cashout operations.
+ cashoutFiatVolume: Amount;
+
+ // How many payments were made to a Taler exchange by another
+ // bank account.
+ talerInCount: Integer;
+
+ // Overall volume that has been paid to a Taler
+ // exchange by another bank account.
+ talerInVolume: Amount;
+
+ // How many payments were made by a Taler exchange to another
+ // bank account.
+ talerOutCount: Integer;
+
+ // Overall volume that has been paid by a Taler
+ // exchange to another bank account.
+ talerOutVolume: Amount;
+ }
+
+
+Endpoints for Integrated Sub-APIs
+---------------------------------
+
+.. http:any:: /taler-integration/*
+
+ All endpoints under this prefix are specified by the.
+ :doc:`GNU Taler bank integration API </core/api-bank-integration>`.
+ This API handles the communication with Taler wallets.
+
+
+.. http:any:: /accounts/$USERNAME/taler-wire-gateway/*
+
+ All endpoints under this prefix are specified
+ by the :doc:`GNU Taler wire gateway API </core/api-bank-wire>`.
+
+ The endpoints are only available for accounts configured with ``is_taler_exchange=true``.
+
+
+.. http:any:: /accounts/$USERNAME/taler-revenue/*
+
+ All endpoints under this prefix are specified
+ by the :doc:`GNU Taler Revenue API </core/api-bank-revenue>`.
+
+
+.. http:any:: /conversion-info/*
+
+ All endpoints under this prefix are specified
+ by the :doc:`GNU Taler Conversion Info API </core/api-bank-conversion-info>`.
+
+
+.. http:post:: /ebicshost
+
+ EBICS base URL. This URL allows clients to make EBICS requests to one of
+ the configured EBICS hosts.
+
+ The Taler bank can be configured to serve bank account transactions and
+ allow payment initiations via the EBICS protocol.
+
+ This is an optional feature, not all implementations of the API support it.
diff --git a/core/api-donau.rst b/core/api-donau.rst
new file mode 100644
index 00000000..09f644ec
--- /dev/null
+++ b/core/api-donau.rst
@@ -0,0 +1,615 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Christian Grothoff
+ @author Pius Loosli
+ @author Lukas Matyja
+ @author Johannes Casaburi
+
+=====================
+The Donau RESTful API
+=====================
+
+The API specified here follows the :ref:`general conventions <http-common>`
+for all details not specified in the individual requests.
+The `glossary <https://docs.taler.net/glossary.html#glossary>`_
+defines all specific terms used in this section.
+
+.. contents:: Table of Contents
+ :local:
+
+.. _donau-overview:
+
+------------
+API Overview
+------------
+
+This is intended to provide a quick overview of the whole REST API. For a more detailed view of the protocol, see the protocol specification.
+
+The chapters group the families of requests frequently encountered when using the Donau API:
+
+* :ref:`Status information<donau_status>`: get the public signing keys of the Donau, the donation unit key, the Donaus config or some entropy
+* :ref:`Issue receipts<donau_issue>`: For use by charities: Issue receipts for blinded unique donor ids.
+* :ref:`Submit receipts<donau_submit>`: Receive the receipts and, if valid, add all of it's donation units to the donor total. Returns a signature on the total yearly donation amount, hash of taxid+salt and year.
+* :ref:`Charity administration and status information<donau_charity>`:
+
+ * For use by administrators to add/modify a charity
+ * For use by charities to get their remaining donation volume
+
+.. include:: tos.rst
+
+.. _donau_status:
+
+----------------------------------------
+Donau public keys and status information
+----------------------------------------
+
+This API is used by donors and charities to obtain global information about
+the Donau, such as online signing keys and available donation units. This is
+typically the first call any Donau client makes, as it returns information
+required to process all of the other interactions with the Donau. The
+returned information is secured by signature(s) from the Donau, especially the
+long-term offline signing key of the Donau, which clients should cache.
+
+.. http:get:: /keys
+
+ Get a list of all donation units keys offered by the Donau,
+ as well as the Donau's current online signing key (used for donation statements).
+
+ **Request:**
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The Donau responds with a `DonauKeysResponse` object. This request should
+ virtually always be successful. It only fails if the Donau is misconfigured.
+
+ **Details:**
+
+ .. ts:def:: DonauKeysResponse
+
+ interface DonauKeysResponse {
+ // libtool-style representation of the Donau protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Financial domain this Donau operates for.
+ domain: string;
+
+ // The Donau's base URL.
+ base_url: string;
+
+ // The Donau's currency.
+ currency: string;
+
+ // How many digits should the amounts be rendered
+ // with by default. Small capitals should
+ // be used to render fractions beyond the number
+ // given here (like on gas stations).
+ currency_fraction_digits: Integer;
+
+ // Donation Units offered by this Donau
+ donation_units: DonationUnitKeyGroup[];
+
+ // The Donau's signing keys.
+ signkeys: SignKey[];
+
+ }
+
+ .. ts:def:: DonationUnitKeyGroup
+
+ type DonationUnitKeyGroup =
+ | DonationUnitKeyGroupRsa
+ | DonationUnitKeyGroupCs;
+
+ .. ts:def:: DonationUnitKeyGroupRsa
+
+ interface DonationUnitKeyGroupRsa extends DonationUnitKeyGroupCommon {
+ cipher: "RSA";
+
+ donation_units: ({
+ rsa_pub: RsaPublicKey;
+ } & DonationUnitKeyCommon)[];
+ }
+
+ .. ts:def:: DonationUnitKeyGroupCs
+
+ interface DonationUnitKeyGroupCs extends DonationUnitKeyGroupCommon {
+ cipher: "CS";
+
+ donation_units: ({
+ cs_pub: Cs25519Point;
+ } & DonationUnitKeyCommon)[];
+ }
+
+ .. ts:def:: DonationUnitKeyGroupCommon
+
+ // Common attributes for all donation unit groups
+ interface DonationUnitKeyGroupCommon {
+ // How much was donated based on this donation receipt.
+ value: Amount;
+
+ }
+
+ .. ts:def:: DonationUnitKeyCommon
+
+ interface DonationUnitKeyCommon {
+
+ // For which year is this donation unit key valid.
+ year: Integer;
+
+ // Set to 'true' if the Donau somehow "lost" the private key. The donation unit was not
+ // revoked, but still cannot be used to withdraw receipts at this time (theoretically,
+ // the private key could be recovered in the future; receipts signed with the private key
+ // remain valid).
+ lost?: boolean;
+ }
+
+ .. ts:def:: DonationUnitKey
+
+ type DonationUnitKey =
+ | RsaDonationUnitKey
+ | CSDonationUnitKey;
+
+ .. ts:def:: RsaDonationUnitKey
+
+ interface RsaDonationUnitKey {
+ cipher: "RSA";
+
+ // RSA public key
+ rsa_public_key: RsaPublicKey;
+ }
+
+ .. ts:def:: CSDonationUnitKey
+
+ interface CSDonationUnitKey {
+ cipher: "CS";
+
+ // Public key of the donation unit.
+ cs_public_key: Cs25519Point;
+
+ }
+
+ A signing key in the ``signkeys`` list is a JSON object with the following fields:
+
+ .. ts:def:: SignKey
+
+ interface SignKey {
+ // The actual Donau's EdDSA signing public key.
+ key: EddsaPublicKey;
+
+ // Initial validity date for the signing key.
+ year: Integer;
+
+ }
+
+
+ .. note::
+
+ Both the individual donation units *and* the donation units list is signed,
+ allowing customers to prove that they received an inconsistent list.
+
+.. http:get:: /seed
+
+ Return an entropy seed. The Donau will return a high-entropy
+ value that will differ for every call. The response is NOT in
+ JSON, but simply high-entropy binary data in the HTTP body.
+ This API should be used by wallets to guard themselves against
+ running on low-entropy (bad PRNG) hardware. Naturally, the entropy
+ returned MUST be mixed with locally generated entropy.
+
+.. http:get:: /config
+
+ Return the protocol version, financial domain and currency supported by this
+ Donau backend.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body is a `DonauVersionResponse`.
+
+ .. ts:def:: DonauVersionResponse
+
+ interface DonauVersionResponse {
+ // libtool-style representation of the Donau protocol version, see
+ // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
+ // The format is "current:revision:age".
+ version: string;
+
+ // Name of the protocol.
+ name: "taler-donau";
+
+ // Currency supported by this Donau.
+ currency: string;
+
+ // Financial domain by this Donau.
+ domain: string;
+
+ }
+
+
+.. _donau_issue:
+
+--------------
+Issue receipts
+--------------
+
+Inspired by the Taler exchange :ref:`Withdrawal<exchange-withdrawal>`.
+
+This API is used by the charity to obtain valid, attested donation receipts from the Donau.
+Use the :ref:`charity GET route<donau_charity_get>` to see the remaining donation volume for the current year.
+
+
+All incoming `BDID` are recorded under the corresponding charity_id by the Donau.
+
+.. http:POST:: /batch-issue/$CHARITY_ID
+
+ Send in a `IssueReceiptsRequest` and ask the Donau to sign all it's contained `BDID`.
+
+ **Request:** `IssueReceiptsRequest`
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `BSDonationReceipts`.
+ :http:statuscode:`403 Forbidden`:
+ The charity signature is invalid. This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`. This suggests a bug in the donor as it should have used current donation unit keys from :ref:`/keys<donau_status>`.
+ :http:statuscode:`409 Conflict`:
+ The donation volume of the charity is not sufficient to issue donation receipts vor all sent in blinded udids. The response is a `IssueError` object.
+ :http:statuscode:`410 Gone`:
+ The requested donation unit key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly.
+
+ **Details:**
+
+ .. ts:def:: IssueReceiptsRequest
+
+ interface IssueReceiptsRequest {
+ charity_signature: EddsaSignature;
+ year: Integer;
+ bdids: BDID[];
+ }
+
+ .. ts:def:: BDID
+
+ interface BDID {
+ donau_pub_hash: HashCode;
+ taxpayer_blinded_id: BDIDEnvelope;
+ }
+
+ .. ts:def:: BDIDEnvelope
+
+ type BDIDEnvelope = RSABDIDEnvelope | CSBDIDEnvelope ;
+
+ .. ts:def:: RSABDIDEnvelope
+
+ interface RSABDIDEnvelope {
+ cipher: "RSA" | "RSA+age_restricted";
+ rsa_blinded_UDID: string; // Crockford Base32 encoded
+ }
+
+ .. ts:def:: CSBDIDEnvelope
+
+ // For donation unit signatures based on Blind Clause-Schnorr, the UDID
+ // consists of the public nonce and two Curve25519 scalars which are two
+ // blinded challenges in the Blinded Clause-Schnorr signature scheme.
+ // See https://taler.net/papers/cs-thesis.pdf for details.
+ interface CSBDIDEnvelope {
+ cipher: "CS" | "CS+age_restricted";
+ cs_nonce: string; // Crockford Base32 encoded
+ cs_blinded_c0: string; // Crockford Base32 encoded
+ cs_blinded_c1: string; // Crockford Base32 encoded
+ }
+
+ .. ts:def:: BDIDBlindingKeyP
+
+ // Secret for blinding/unblinding.
+ // An RSA blinding secret, which is basically
+ // a 256-bit nonce, converted to Crockford Base32.
+ type BDIDBlindingKeyP = string;
+
+ .. ts:def:: BSDonationReceipts
+
+ interface DonationReceipts {
+ blind_signed_receipt_signatures: DonationReceiptSignature[];
+ }
+
+ .. ts:def:: DonationReceiptSignature
+
+ .. ts:def:: BlindedDonationReceiptSignature
+
+ type BlindedDonationReceiptSignature =
+ | RSABlindedDonationReceiptSignature
+ | CSBlindedDonationReceiptSignature;
+
+ .. ts:def:: RSABlindedDonationReceiptSignature
+
+ interface RSABlindedDonationReceiptSignature {
+ cipher: "RSA";
+
+ // (blinded) RSA signature
+ blinded_rsa_signature: BlindedRsaSignature;
+ }
+
+ .. ts:def:: CSBlindedDonationReceiptSignature
+
+ interface CSBlindedDonationReceiptSignature {
+ type: "CS";
+
+ // Signer chosen bit value, 0 or 1, used
+ // in Clause Blind Schnorr to make the
+ // ROS problem harder.
+ b: Integer;
+
+ // Blinded scalar calculated from c_b.
+ s: Cs25519Scalar;
+ }
+
+
+ type DonationReceiptSignature = RSADonationReceiptSignature | CSDonationReceiptSignature ;
+
+ .. ts:def:: RSADonationReceiptSignature
+
+ interface RSADonationReceiptSignature {
+ cipher: "RSA";
+ rsa_blinded_donation_receipt_sig: string; // Crockford Base32 encoded
+ }
+
+ .. ts:def:: CSDonationReceiptSignature
+
+ interface CSDonationReceiptSignature {
+ cipher: "CS";
+ cs_nonce: string; // Crockford Base32 encoded
+ cs_blinded_c0: string; // Crockford Base32 encoded
+ cs_blinded_c1: string; // Crockford Base32 encoded
+ }
+
+ .. ts:def:: IssueError
+
+ interface IssueError{
+ max_per_year: Amount;
+ current_year: Amount;
+ }
+
+ .. ts:def:: DonationUnitUnknownError
+
+ interface DonationUnitUnknownError{
+ unknown_hash_pub_donation_unit: HashCode[];
+ donau_pub: EddsaPublicKey;
+ donau_sig: EddsaSignature;
+ }
+
+ .. ts:def:: DonationUnitExpiredMessage
+
+ interface DonationUnitExpiredMessage{
+ h_donation_unit_pub: HashCode;
+ donau_pub: EddsaPublicKey;
+ donau_sig: EddsaSignature;
+ }
+
+.. _donau_submit:
+
+---------------
+Submit receipts
+---------------
+
+Inspired by the Taler exchange :ref:`Deposit<deposit-par>`.
+
+.. http:POST:: /submit
+
+ Send in donation receipts for the past fiscal year, receive signed total back.
+
+ **Request:** `SubmitDonationReceiptsRequest`
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `SubmitResponse`.
+ :http:statuscode:`403 Forbidden`:
+ One of the signatures is invalid. This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`.
+ :http:statuscode:`410 Gone`:
+ The requested donation unit key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly. FIXME: text does not match our use case well.
+
+ **Details:**
+
+ .. ts:def:: SubmitDonationReceiptsRequest
+
+ interface SubmitDonationReceiptsRequest{
+ // hashed taxpayer ID plus salt
+ taxnr_hashed: HashCode;
+ // All donation receipts must be for this year.
+ year: Integer;
+ // Receipts should be sorted by amount.
+ donation_receipts: DonationReceipt[];
+ }
+
+ .. ts:def:: DonationReceipt
+
+ interface DonationReceipt{
+ donation_unit_pub_hash: HashCode;
+ nonce: string;
+ donau_sig: DonationSignature
+ }
+
+ .. ts:def:: DonationSignature
+
+ type DonationSignature =
+ RsaDonationSignature | CSDonationSignature;
+
+ .. ts:def:: RsaDonationSignature
+
+ interface RsaDonationSignature {
+ cipher: "RSA";
+
+ // RSA signature
+ rsa_signature: RsaSignature;
+ }
+
+ .. ts:def:: CSDonationSignature
+
+ interface CSDonationSignature {
+ type: "CS";
+
+ // R value component of the signature.
+ cs_signature_r: Cs25519Point;
+
+ // s value component of the signature.
+ cs_signature_s: Cs25519Scalar:
+ }
+
+
+
+ .. ts:def:: SubmitResponse
+
+ interface SubmitResponse{
+ // *accepted* total
+ total: Amount;
+ // signature over taxid_hashed, total, year
+ signature: EddsaSignature;
+ }
+
+.. _donau_charity:
+
+---------------------------------------------
+Charity administration and status information
+---------------------------------------------
+
+The administration requests require an authorized bearer token to be set in the HTTP "Authorization" Header. This token can be set by a proxy validating authentication/authorization (using e.g. LDAP).
+The GET status requests require an authorized bearer token as well.
+
+.. http:GET:: /charities
+
+ GET all charities. Only allowed if the request comes with the administration bearer token.
+
+ return all charities
+
+ **Request:**
+
+ **Reponse:**
+
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `Charities`.
+
+ **Details:**
+
+ .. ts:def:: Charities
+
+ interface Charities{
+ charities: CharitySummary[];
+ }
+
+ .. ts:def:: CharitySummary
+
+ interface CharitySummary{
+ charity_id: Integer;
+ name: string;
+ max_per_year: Amount;
+ receipts_to_date: Amount;
+ }
+
+.. _donau_charity_get:
+
+.. http:get:: /charities/$CHARITY_ID
+
+ GET a specific charity. Only allowed if the request comes with the charity or administration bearer token.
+
+ Request information about a charity.
+
+ **Request:**
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The Donau responds with a `Charity` object
+ :http:statuscode:`404 Not found`:
+ The charity id does not belong to a charity known to the Donau.
+
+ .. ts:def:: Charity
+
+ interface Charity {
+ charity_pub: EddsaPublicKey;
+ name: string;
+ url: string;
+ max_per_year: Amount;
+ receipts_to_date: Amount;
+ current_year: Integer;
+ }
+
+.. http:POST:: /charity
+
+ Add a charity. Only allowed if the request comes with the administrator bearer token.
+
+ **Request:** `CharityRequest`
+
+ **Response:**
+
+ **Details:**
+
+ :http:statuscode:`201 Created`:
+ The request was successful, and the response is a `CharityResponse`.
+
+ :http:statuscode:`403 Forbidden`:
+ The request did not contain an accepted administrator bearer token in it's header.
+
+ .. ts:def:: CharityRequest
+
+ interface CharityRequest{
+ charity_pub: EddsaPublicKey;
+ name: string;
+ url: string;
+ max_per_year: Amount;
+ receipts_to_date: Amount;
+ current_year: Integer;
+ }
+
+ .. ts:def:: CharityResponse
+
+ interface CharityResponse{
+ id: Integer;
+ }
+
+
+.. http:PATCH:: /charities/{id}
+
+ Modify a charity. Only allowed if the request comes with the administrator bearer token.
+
+ **Request:** `CharityRequest`
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request was successful.
+
+ :http:statuscode:`403 Forbidden`:
+ The request did not contain an accepted administrator bearer token in it's header.
+
+
+.. http:DELETE:: /charities/{id}
+
+ Delete (or deactivate) a charity. Only allowed if the request comes with the administrator bearer token.
+
+ **Request:**
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The request was successful.
+
+ :http:statuscode:`403 Forbidden`:
+ The request did not contain an accepted administrator bearer token in it's header.
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
index 19090067..5a734795 100644
--- a/core/api-exchange.rst
+++ b/core/api-exchange.rst
@@ -1,6 +1,6 @@
..
This file is part of GNU TALER.
- Copyright (C) 2014-2023 Taler Systems SA
+ Copyright (C) 2014-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -24,6 +24,9 @@ for all details not specified in the individual requests.
The `glossary <https://docs.taler.net/glossary.html#glossary>`_
defines all specific terms used in this section.
+.. contents:: Table of Contents
+ :local:
+
.. include:: tos.rst
.. _keys:
@@ -55,7 +58,11 @@ possibly by using HTTPS.
.. http:get:: /config
- Return the protocol version and currency supported by this exchange backend, as well as the list of possible KYC requirements. This endpoint is largely for the SPA for AML officers. Merchants should use ``/keys`` which also contains the protocol version and currency.
+ Return the protocol version and currency supported by this exchange backend,
+ as well as the list of possible KYC requirements. This endpoint is largely
+ for the SPA for AML officers. Merchants should use ``/keys`` which also
+ contains the protocol version and currency.
+ This specification corresponds to ``current`` protocol being **v19**.
**Response:**
@@ -73,14 +80,50 @@ possibly by using HTTPS.
// Name of the protocol.
name: "taler-exchange";
- // Currency supported by this exchange.
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since **v18**, may become mandatory in the future.
+ implementation?: string;
+
+ // Currency supported by this exchange, given
+ // as a currency code ("USD" or "EUR").
currency: string;
+ // How wallets should render this currency.
+ currency_specification: CurrencySpecification;
+
// Names of supported KYC requirements.
supported_kyc_requirements: string[];
}
+ .. ts:def:: CurrencySpecification
+
+ interface CurrencySpecification {
+ // Name of the currency. Like "US Dollar".
+ name: string;
+
+ // Code of the currency.
+ // Deprecated in protocol **v18** for the exchange
+ // and in protocol v6 for the merchant.
+ currency: 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, if needed by
+ // padding with zeros.
+ num_fractional_trailing_zero_digits: Integer;
+
+ // map of powers of 10 to alternative currency names / symbols, must
+ // always have an entry under "0" that defines the base name,
+ // e.g. "0 => €" or "3 => k€". For BTC, would be "0 => BTC, -3 => mBTC".
+ // Communicates the currency symbol to be used.
+ alt_unit_names: { log10 : string };
+ }
+
.. http:get:: /keys
@@ -114,11 +157,49 @@ possibly by using HTTPS.
// The exchange's currency or asset unit.
currency: string;
+ // How wallets should render this currency.
+ currency_specification: CurrencySpecification;
+
+ // Absolute cost offset for the STEFAN curve used
+ // to (over) approximate fees payable by amount.
+ stefan_abs: Amount;
+
+ // Factor to multiply the logarithm of the amount
+ // with to (over) approximate fees payable by amount.
+ // Note that the total to be paid is first to be
+ // divided by the smallest denomination to obtain
+ // the value that the logarithm is to be taken of.
+ stefan_log: Amount;
+
+ // Linear cost factor for the STEFAN curve used
+ // to (over) approximate fees payable by amount.
+ //
+ // Note that this is a scalar, as it is multiplied
+ // with the actual amount.
+ stefan_lin: Float;
+
// Type of the asset. "fiat", "crypto", "regional"
// or "stock". Wallets should adjust their UI/UX
// based on this value.
asset_type: string;
+ // Array of wire accounts operated by the exchange for
+ // incoming wire transfers.
+ accounts: WireAccount[];
+
+ // Object mapping names of wire methods (i.e. "iban" or "x-taler-bank")
+ // to wire fees.
+ wire_fees: { method : AggregateTransferFee[] };
+
+ // List of exchanges that this exchange is partnering
+ // with to enable wallet-to-wallet transfers.
+ wads: ExchangePartner[];
+
+ // Set to true if this exchange allows the use
+ // of reserves for rewards.
+ // @deprecated in protocol **v18**.
+ rewards_allowed: false;
+
// EdDSA master public key of the exchange, used to sign entries
// in ``denoms`` and ``signkeys``.
master_public_key: EddsaPublicKey;
@@ -133,18 +214,20 @@ possibly by using HTTPS.
// Currency must match ``currency``.
wallet_balance_limit_without_kyc?: Amount[];
- // Denominations offered by this exchange.
- // DEPRECATED: Will eventually be replaced by the
- // differently structured "denominations" field.
- denoms: Denom[];
-
// Denominations offered by this exchange
denominations: DenomGroup[];
- // Compact EdDSA `signature` (binary-only) over the XOR of all
- // .hash fields (in binary) in the list "denominations".
- // Signature of `TALER_ExchangeKeySetPS`
- denominations_sig: EddsaSignature;
+ // Compact EdDSA `signature` (binary-only) over the
+ // contatentation of all of the master_sigs (in reverse
+ // chronological order by group) in the arrays under
+ // "denominations". Signature of `TALER_ExchangeKeySetPS`
+ exchange_sig: EddsaSignature;
+
+ // Public EdDSA key of the exchange that was used to generate the signature.
+ // Should match one of the exchange's signing keys from ``signkeys``. It is given
+ // explicitly as the client might otherwise be confused by clock skew as to
+ // which signing key was used for the ``exchange_sig``.
+ exchange_pub: EddsaPublicKey;
// Denominations for which the exchange currently offers/requests recoup.
recoup: Recoup[];
@@ -170,24 +253,90 @@ possibly by using HTTPS.
// The signature has purpose TALER_SIGNATURE_MASTER_EXTENSIONS.
extensions_sig?: EddsaSignature;
- // Compact EdDSA `signature` (binary-only) over the SHA-512 hash of the
- // concatenation of all SHA-512 hashes of the RSA denomination public keys
- // in ``denoms`` in the same order as they were in ``denoms``. Note that for
- // hashing, the binary format of the RSA public keys is used, and not their
- // `base32 encoding <base32>`. Wallets cannot do much with this signature by itself;
- // it is only useful when multiple clients need to establish that the exchange
- // is sabotaging end-user anonymity by giving disjoint denomination keys to
- // different users. If an exchange were to do this, this signature allows the
- // clients to demonstrate to the public that the exchange is dishonest.
- // Signature of `TALER_ExchangeKeySetPS`
- // DEPRICATED: Will eventually replaced by "denominations_sig"
- eddsa_sig: EddsaSignature;
+ }
+
+ The specification for the account object is:
+
+ .. ts:def:: WireAccount
+
+ interface WireAccount {
+ // ``payto://`` URI identifying the account and wire method
+ payto_uri: string;
+
+ // URI to convert amounts from or to the currency used by
+ // this wire account of the exchange. Missing if no
+ // conversion is applicable.
+ conversion_url?: string;
+
+ // Restrictions that apply to bank accounts that would send
+ // funds to the exchange (crediting this exchange bank account).
+ // Optional, empty array for unrestricted.
+ credit_restrictions: AccountRestriction[];
+
+ // Restrictions that apply to bank accounts that would receive
+ // funds from the exchange (debiting this exchange bank account).
+ // Optional, empty array for unrestricted.
+ debit_restrictions: AccountRestriction[];
+
+ // Signature using the exchange's offline key over
+ // a `TALER_MasterWireDetailsPS`
+ // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DETAILS``.
+ master_sig: EddsaSignature;
+
+ // Display label wallets should use to show this
+ // bank account.
+ // Since protocol **v19**.
+ bank_label?: string;
+
+ // *Signed* integer with the display priority for
+ // this bank account. Optional, 0 if missing.
+ // Since protocol **v19**.
+ priority?: Integer;
+
+ }
+
+ .. ts:def:: AccountRestriction
+
+ type AccountRestriction =
+ | RegexAccountRestriction
+ | DenyAllAccountRestriction
+
+ .. ts:def:: DenyAllAccountRestriction
+
+ // Account restriction that disables this type of
+ // account for the indicated operation categorically.
+ interface DenyAllAccountRestriction {
+
+ type: "deny";
+ }
+
+ .. 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. The regular expression
+ // should follow posix-egrep, but without support for character
+ // classes, GNU extensions, back-references or intervals. See
+ // https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
+ // for a description of the posix-egrep syntax. Applications
+ // may support regexes with additional features, but exchanges
+ // must not use such regexes.
+ payto_regex: string;
+
+ // Hint for a human to understand the restriction
+ // (that is hopefully easier to comprehend than the regex itself).
+ human_hint: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // human hints.
+ human_hint_i18n?: { [lang_tag: string]: string };
- // Public EdDSA key of the exchange that was used to generate the signature.
- // Should match one of the exchange's signing keys from ``/keys``. It is given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- eddsa_pub: EddsaPublicKey;
}
.. ts:def:: GlobalFees
@@ -237,7 +386,6 @@ possibly by using HTTPS.
}
-
.. ts:def:: AgeMask
// Binary representation of the age groups.
@@ -323,10 +471,6 @@ possibly by using HTTPS.
// Fee charged by the exchange for refunding a coin of this denomination.
fee_refund: Amount;
- // XOR of all the SHA-512 hash values of the denominations' public keys
- // in this group. Note that for hashing, the binary format of the
- // public keys is used, and not their base32 encoding.
- hash: HashCode;
}
.. ts:def:: DenomCommon
@@ -338,51 +482,27 @@ possibly by using HTTPS.
// When does the denomination key become valid?
stamp_start: Timestamp;
- // When is it no longer possible to deposit coins
+ // When is it no longer possible to withdraw coins
// of this denomination?
stamp_expire_withdraw: Timestamp;
- // Timestamp indicating by when legal disputes relating to these coins must
- // be settled, as the exchange will afterwards destroy its evidence relating to
- // transactions involving this coin.
- stamp_expire_legal: Timestamp;
- }
-
- .. ts:def:: Denom
-
- interface Denom {
- // How much are coins of this denomination worth?
- value: Amount;
-
- // When does the denomination key become valid?
- stamp_start: Timestamp;
-
// When is it no longer possible to deposit coins
// of this denomination?
- stamp_expire_withdraw: Timestamp;
+ stamp_expire_deposit: Timestamp;
// Timestamp indicating by when legal disputes relating to these coins must
// be settled, as the exchange will afterwards destroy its evidence relating to
// transactions involving this coin.
stamp_expire_legal: Timestamp;
- // Public key for the denomination.
- denom_pub: DenominationKey;
-
- // Fee charged by the exchange for withdrawing a coin of this denomination.
- fee_withdraw: Amount;
-
- // Fee charged by the exchange for depositing a coin of this denomination.
- fee_deposit: Amount;
-
- // Fee charged by the exchange for refreshing a coin of this denomination.
- fee_refresh: Amount;
-
- // Fee charged by the exchange for refunding a coin of this denomination.
- fee_refund: Amount;
-
- // Signature of `TALER_DenominationKeyValidityPS`.
- master_sig: EddsaSignature;
+ // Set to 'true' if the exchange somehow "lost"
+ // the private key. The denomination was not
+ // necessarily revoked, but still cannot be used
+ // to withdraw coins at this time (theoretically,
+ // the private key could be recovered in the
+ // future; coins signed with the private key
+ // remain valid).
+ lost?: boolean;
}
.. ts:def:: DenominationKey
@@ -506,53 +626,6 @@ possibly by using HTTPS.
Both the individual denominations *and* the denomination list is signed,
allowing customers to prove that they received an inconsistent list.
-.. _wire-req:
-
-.. http:get:: /wire
-
- Returns a list of payment methods supported by the exchange. The idea is that wallets may use this information to instruct users on how to perform wire transfers to top up their wallets.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The exchange responds with a `WireResponse` object. This request should virtually always be successful.
-
- **Details:**
-
- .. ts:def:: WireResponse
-
- interface WireResponse {
-
- // Master public key of the exchange, must match the key returned in ``/keys``.
- master_public_key: EddsaPublicKey;
-
- // Array of wire accounts operated by the exchange for
- // incoming wire transfers.
- accounts: WireAccount[];
-
- // Object mapping names of wire methods (i.e. "iban" or "x-taler-bank")
- // to wire fees.
- fees: { method : AggregateTransferFee };
-
- // List of exchanges that this exchange is partnering
- // with to enable wallet-to-wallet transfers.
- wads: ExchangePartner[];
- }
-
- The specification for the account object is:
-
- .. ts:def:: WireAccount
-
- interface WireAccount {
- // ``payto://`` URI identifying the account and wire method
- payto_uri: string;
-
- // Signature using the exchange's offline key over
- // a `TALER_MasterWireDetailsPS`
- // with purpose ``TALER_SIGNATURE_MASTER_WIRE_DETAILS``.
- master_sig: EddsaSignature;
- }
-
Aggregate wire transfer fees representing the fees the exchange
charges per wire transfer to a merchant must be specified as an
array in all wire transfer response objects under ``fees``. The
@@ -567,9 +640,6 @@ possibly by using HTTPS.
// Per transfer closing fee.
closing_fee: Amount;
- // Per exchange-to-exchange transfer (wad) fee.
- wad_fee: Amount;
-
// What date (inclusive) does this fee go into effect?
// The different fees must cover the full time period in which
// any of the denomination keys are valid without overlap.
@@ -594,6 +664,9 @@ possibly by using HTTPS.
// Public master key of the partner exchange.
partner_master_pub: EddsaPublicKey;
+ // Per exchange-to-exchange transfer (wad) fee.
+ wad_fee: Amount;
+
// Exchange-to-exchange wad (wire) transfer frequency.
wad_frequency: RelativeTime;
@@ -678,8 +751,8 @@ Management operations authorized by master key
// transactions involving this coin.
stamp_expire_legal: Timestamp;
- // Public (RSA) key for the denomination.
- denom_pub: RsaPublicKey;
+ // Public key for the denomination.
+ denom_pub: DenominationKey;
// Fee charged by the exchange for withdrawing a coin of this denomination.
fee_withdraw: Amount;
@@ -760,7 +833,7 @@ Management operations authorized by master key
interface DenomSignature {
- // Hash of the public (RSA) key of the denomination.
+ // Hash of the public key of the denomination.
h_denom_pub: HashCode;
// Signature over `TALER_DenominationKeyValidityPS`.
@@ -1040,6 +1113,16 @@ Management operations authorized by master key
// become active immediately! Used ONLY to detect replay attacks.
validity_start: Timestamp;
+ // Display label wallets should use to show this
+ // bank account.
+ // Since protocol **v19**.
+ bank_label?: string;
+
+ // *Signed* integer with the display priority for
+ // this bank account.
+ // Since protocol **v19**.
+ priority?: Integer;
+
}
.. http:post:: /management/wire/disable
@@ -1414,8 +1497,7 @@ and freeze or unfreeze accounts suspected of money laundering.
// Numerical values are defined in `AmlDecisionState`.
new_state: Integer;
- // Signature by the AML officer over a
- // `TALER_MasterAmlOfficerStatusPS`.
+ // Signature by the AML officer over a `TALER_AmlDecisionPS`.
// Must have purpose ``TALER_SIGNATURE_MASTER_AML_KEY``.
officer_sig: EddsaSignature;
@@ -1500,6 +1582,7 @@ This part of the API is for the use by auditors interacting with the exchange.
}
+.. _exchange-withdrawal:
----------
Withdrawal
@@ -1524,7 +1607,7 @@ exchange.
.. http:get:: /reserves/$RESERVE_PUB
- Request information about a reserve.
+ Request summary information about a reserve.
**Request:**
@@ -1555,417 +1638,6 @@ exchange.
}
-.. http:post:: /reserves/$RESERVE_PUB/status
-
- Request information about a reserve or an account.
-
- **Request:**
-
- The request body must be a `ReserveStatusRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange.
- :http:statuscode:`403 Forbidden`:
- The *TALER_SIGNATURE_RESERVE_STATUS_REQUEST* signature is invalid.
- This response comes with a standard `ErrorDetail` response. Alternatively, the provided timestamp is not close to the current time.
- :http:statuscode:`404 Not found`:
- The reserve key does not belong to a reserve known to the exchange.
-
- **Details:**
-
- .. ts:def:: ReserveStatusRequest
-
- interface ReserveStatusRequest {
- // Signature of purpose
- // ``TALER_SIGNATURE_RESERVE_STATUS_REQUEST`` over
- // a `TALER_ReserveStatusRequestSignaturePS`.
- reserve_sig: EddsaSignature;
-
- // Time when the client made the request.
- // Timestamp must be reasonably close to the time of
- // the exchange, otherwise the exchange may reject
- // the request.
- request_timestamp: Timestamp;
- }
-
- .. ts:def:: ReserveStatus
-
- interface ReserveStatus {
- // Balance left in the reserve.
- balance: Amount;
-
- // If set, gives the maximum age group that the client is required to set
- // during withdrawal.
- maximum_age_group: number;
-
- // Transaction history for this reserve.
- // May be partial (!).
- history: TransactionHistoryItem[];
- }
-
- Objects in the transaction history have the following format:
-
- .. ts:def:: TransactionHistoryItem
-
- // Union discriminated by the "type" field.
- type TransactionHistoryItem =
- | AccountSetupTransaction
- | ReserveHistoryTransaction
- | ReserveWithdrawTransaction
- | ReserveAgeWithdrawTransaction
- | ReserveCreditTransaction
- | ReserveClosingTransaction
- | ReserveOpenRequestTransaction
- | ReserveCloseRequestTransaction
- | PurseMergeTransaction;
-
- .. ts:def:: AccountSetupTransaction
-
- interface AccountSetupTransaction {
- type: "SETUP";
-
- // KYC fee agreed to by the reserve owner.
- kyc_fee: Amount;
-
- // Time when the KYC was triggered.
- kyc_timestamp: Timestamp;
-
- // Hash of the wire details of the account.
- // Note that this hash is unsalted and potentially
- // private (as it could be inverted), hence access
- // to this endpoint must be authorized using the
- // private key of the reserve.
- h_wire: HashCode;
-
- // Signature created with the reserve's private key.
- // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST`` over
- // a ``TALER_AccountSetupRequestSignaturePS``.
- reserve_sig: EddsaSignature;
-
- }
-
- .. ts:def:: ReserveHistoryTransaction
-
- interface ReserveHistoryTransaction {
- type: "HISTORY";
-
- // Fee agreed to by the reserve owner.
- amount: Amount;
-
- // Time when the request was made.
- request_timestamp: Timestamp;
-
- // Signature created with the reserve's private key.
- // Must be of purpose ``TALER_SIGNATURE_RESERVE_HISTORY_REQUEST`` over
- // a `TALER_ReserveHistoryRequestSignaturePS`.
- reserve_sig: EddsaSignature;
-
- }
-
- .. ts:def:: ReserveWithdrawTransaction
-
- interface ReserveWithdrawTransaction {
- type: "WITHDRAW";
-
- // Amount withdrawn.
- amount: Amount;
-
- // Hash of the denomination public key of the coin.
- h_denom_pub: HashCode;
-
- // Hash of the blinded coin to be signed.
- h_coin_envelope: HashCode;
-
- // Signature over a `TALER_WithdrawRequestPS`
- // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW``
- // created with the reserve's private key.
- reserve_sig: EddsaSignature;
-
- // Fee that is charged for withdraw.
- withdraw_fee: Amount;
- }
-
- .. ts:def:: ReserveAgeWithdrawTransaction
-
- interface ReserveAgeWithdrawTransaction {
- type: "AGEWITHDRAW";
-
- // Total Amount withdrawn.
- amount: Amount;
-
- // Commitment of all ``n*kappa`` blinded coins.
- h_commitment: HashCode;
-
- // Signature over a `TALER_AgeWithdrawRequestPS`
- // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW``
- // created with the reserve's private key.
- reserve_sig: EddsaSignature;
-
- // Fee that is charged for withdraw.
- withdraw_fee: Amount;
- }
-
-
- .. ts:def:: ReserveCreditTransaction
-
- interface ReserveCreditTransaction {
- type: "CREDIT";
-
- // Amount deposited.
- amount: Amount;
-
- // Sender account ``payto://`` URL.
- sender_account_url: string;
-
- // Opaque identifier internal to the exchange that
- // uniquely identifies the wire transfer that credited the reserve.
- wire_reference: Integer;
-
- // Timestamp of the incoming wire transfer.
- timestamp: Timestamp;
- }
-
-
- .. ts:def:: ReserveClosingTransaction
-
- interface ReserveClosingTransaction {
- type: "CLOSING";
-
- // Closing balance.
- amount: Amount;
-
- // Closing fee charged by the exchange.
- closing_fee: Amount;
-
- // Wire transfer subject.
- wtid: Base32;
-
- // ``payto://`` URI of the wire account into which the funds were returned to.
- receiver_account_details: string;
-
- // This is a signature over a
- // struct `TALER_ReserveCloseConfirmationPS` with purpose
- // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``.
- exchange_sig: EddsaSignature;
-
- // Public key used to create 'exchange_sig'.
- exchange_pub: EddsaPublicKey;
-
- // Time when the reserve was closed.
- timestamp: Timestamp;
- }
-
-
- .. ts:def:: ReserveOpenRequestTransaction
-
- interface ReserveOpenRequestTransaction {
- type: "OPEN";
-
- // Open fee paid from the reserve.
- open_fee: Amount;
-
- // This is a signature over
- // a struct `TALER_ReserveOpenPS` with purpose
- // ``TALER_SIGNATURE_WALLET_RESERVE_OPEN``.
- reserve_sig: EddsaSignature;
-
- // Timestamp of the open request.
- request_timestamp: Timestamp;
-
- // Requested expiration.
- requested_expiration: Timestamp;
-
- // Requested number of free open purses.
- requested_min_purses: Integer;
-
- }
-
- .. ts:def:: ReserveCloseRequestTransaction
-
- interface ReserveCloseRequestTransaction {
- type: "CLOSE";
-
- // This is a signature over
- // a struct `TALER_ReserveClosePS` with purpose
- // ``TALER_SIGNATURE_WALLET_RESERVE_CLOSE``.
- reserve_sig: EddsaSignature;
-
- // Target account ``payto://``, optional.
- h_payto?: PaytoHash;
-
- // Timestamp of the close request.
- request_timestamp: Timestamp;
- }
-
- .. ts:def:: ReserveCreditTransaction
-
- interface ReserveCreditTransaction {
- type: "CREDIT";
-
- // Amount deposited.
- amount: Amount;
-
- // Sender account ``payto://`` URL.
- sender_account_url: string;
-
- // Opaque identifier internal to the exchange that
- // uniquely identifies the wire transfer that credited the reserve.
- wire_reference: Integer;
-
- // Timestamp of the incoming wire transfer.
- timestamp: Timestamp;
- }
-
- .. ts:def:: PurseMergeTransaction
-
- interface PurseMergeTransaction {
- type: "MERGE";
-
- // SHA-512 hash of the contact of the purse.
- h_contract_terms: HashCode;
-
- // EdDSA public key used to approve merges of this purse.
- merge_pub: EddsaPublicKey;
-
- // Minimum age required for all coins deposited into the purse.
- min_age: Integer;
-
- // Number that identifies who created the purse
- // and how it was paid for.
- flags: Integer;
-
- // Purse public key.
- purse_pub: EddsaPublicKey;
-
- // EdDSA signature of the account/reserve affirming the merge
- // over a `TALER_AccountMergeSignaturePS`.
- // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE``
- reserve_sig: EddsaSignature;
-
- // Client-side timestamp of when the merge request was made.
- merge_timestamp: Timestamp;
-
- // Indicative time by which the purse should expire
- // if it has not been merged into an account. At this
- // point, all of the deposits made should be
- // auto-refunded.
- purse_expiration: Timestamp;
-
- // Purse fee the reserve owner paid for the purse creation.
- purse_fee: Amount;
-
- // Total amount merged into the reserve.
- // (excludes fees).
- amount: Amount;
-
- // True if the purse was actually merged.
- // If false, only the purse_fee has an impact
- // on the reserve balance!
- merged: boolean;
- }
-
-
-.. http:post:: /reserves/$RESERVE_PUB/history
-
- Request information about the full history of
- a reserve or an account.
-
- **Request:**
-
- The request body must be a `ReserveHistoryRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange.
- :http:statuscode:`403 Forbidden`:
- The *TALER_SIGNATURE_RESERVE_HISTORY_REQUEST* is invalid.
- This response comes with a standard `ErrorDetail` response. Alternatively, the provided timestamp is not close to the current time.
- :http:statuscode:`404 Not found`:
- The reserve key does not belong to a reserve known to the exchange.
- :http:statuscode:`412 Precondition failed`:
- The balance in the reserve is insufficient to pay for the history request.
- This response comes with a standard `ErrorDetail` response.
-
- **Details:**
-
- .. ts:def:: ReserveHistoryRequest
-
- interface ReserveHistoryRequest {
- // Signature of type
- // ``TALER_SIGNATURE_RESERVE_HISTORY_REQUEST``
- // over a `TALER_ReserveHistoryRequestSignaturePS`.
- reserve_sig: EddsaSignature;
-
- // Time when the client made the request.
- // Timestamp must be reasonably close to the time of
- // the exchange, otherwise the exchange may reject
- // the request.
- request_timestamp: Timestamp;
- }
-
-
-.. _delete-reserve:
-
-.. http:DELETE:: /reserves/$RESERVE_PUB
-
- Forcefully closes a reserve.
- The request header must contain an *Account-Request-Signature*.
- Note: this endpoint is not currently implemented!
-
- **Request:**
-
- *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to delete the account. The purpose used MUST be ``TALER_SIGNATURE_RESERVE_CLOSE``.
-
- :query force=BOOLEAN: *Optional.* If set to 'true' specified, the exchange
- will delete the account even if there is a balance remaining.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The operation succeeded, the exchange provides details
- about the account deletion.
- The response will include a `ReserveClosedResponse` object.
- :http:statuscode:`403 Forbidden`:
- The *Account-Request-Signature* is invalid.
- This response comes with a standard `ErrorDetail` response.
- :http:statuscode:`404 Not found`:
- The account is unknown to the exchange.
- :http:statuscode:`409 Conflict`:
- The account is still has digital cash in it, the associated
- wire method is ``void`` and the *force* option was not provided.
- This response comes with a standard `ErrorDetail` response.
-
- **Details:**
-
- .. ts:def:: ReserveClosedResponse
-
- interface ReserveClosedResponse {
-
- // Final balance of the account.
- closing_amount: Amount;
-
- // Current time of the exchange, used as part of
- // what the exchange signs over.
- close_time: Timestamp;
-
- // Hash of the wire account into which the remaining
- // balance will be transferred. Note: may be the
- // hash over ``payto://void/`, in which case the
- // balance is forfeit to the profit of the exchange.
- h_wire: HashCode;
-
- // This is a signature over a
- // struct ``TALER_AccountDeleteConfirmationPS`` with purpose
- // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``.
- exchange_sig: EddsaSignature;
-
- }
-
-
-
Withdraw
~~~~~~~~
@@ -2011,7 +1683,7 @@ Withdraw
.. ts:def:: WithdrawPrepareResponse
- type WithdrawPrepareResponse
+ type WithdrawPrepareResponse =
| ExchangeWithdrawValue;
.. ts:def:: ExchangeWithdrawValue
@@ -2039,28 +1711,31 @@ Withdraw
}
-.. http:post:: /reserves/$RESERVE_PUB/withdraw
+Batch Withdraw
+~~~~~~~~~~~~~~
+
+.. http:post:: /reserves/$RESERVE_PUB/batch-withdraw
- Withdraw a coin of the specified denomination. Note that the client should
- commit all of the request details, including the private key of the coin and
- the blinding factor, to disk *before* issuing this request, so that it can
+ Withdraw multiple coins from the same reserve. Note that the client should
+ commit all of the request details, including the private key of the coins and
+ the blinding factors, to disk *before* issuing this request, so that it can
recover the information if necessary in case of transient failures, like
power outage, network outage, etc.
- **Request:** The request body must be a `WithdrawRequest` object.
+ **Request:** The request body must be a `BatchWithdrawRequest` object.
**Response:**
:http:statuscode:`200 OK`:
- The request was successful, and the response is a `WithdrawResponse`. Note that repeating exactly the same request
- will again yield the same response, so if the network goes down during the
- transaction or before the client can commit the coin signature to disk, the
- coin is not lost.
+ The request was successful, and the response is a `BatchWithdrawResponse`.
+ Note that repeating exactly the same request will again yield the same
+ response, so if the network goes down during the transaction or before the
+ client can commit the coin signature to disk, the coin is not lost.
:http:statuscode:`403 Forbidden`:
- The signature is invalid.
+ A signature is invalid.
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
- The denomination key or the reserve are not known to the exchange. If the
+ A denomination key or the reserve are not known to the exchange. If the
denomination key is unknown, this suggests a bug in the wallet as the
wallet should have used current denomination keys from ``/keys``.
In this case, the response will be a `DenominationUnknownMessage`.
@@ -2070,14 +1745,19 @@ Withdraw
In this case, the wallet should repeat the exact same request later again
using exactly the same blinded coin.
:http:statuscode:`409 Conflict`:
- The balance of the reserve is not sufficient to withdraw a coin of the indicated denomination.
- The response is `WithdrawError` object.
+ One of the following reasons occured:
+
+ 1. The balance of the reserve is not sufficient to withdraw the coins of the
+ indicated denominations. The response is `WithdrawError` object.
+
+ 2. The reserve has a birthday set and requires a request to ``/age-withdraw`` instead.
+ The response comes with a standard `ErrorDetail` response with error-code ``TALER_EC_EXCHANGE_RESERVES_AGE_RESTRICTION_REQUIRED`` and an additional field ``maximum_allowed_age`` for the maximum age (in years) that the client can commit to in the call to ``/age-withdraw``
:http:statuscode:`410 Gone`:
- The requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked. The response is a
- `DenominationExpiredMessage`. Clients must evaluate
- the error code provided to understand which of the
- cases this is and handle it accordingly.
+ A requested denomination key is not yet or no longer valid.
+ It either before the validity start, past the expiration or was revoked.
+ The response is a `DenominationExpiredMessage`. Clients must evaluate the
+ error code provided to understand which of the cases this is and handle it
+ accordingly.
:http:statuscode:`451 Unavailable for Legal Reasons`:
This reserve has received funds from a purse or the amount withdrawn
exceeds another legal threshold and thus the reserve must
@@ -2100,41 +1780,18 @@ Withdraw
**Details:**
- .. ts:def:: DenominationExpiredMessage
-
- interface DenominationExpiredMessage {
-
- // Taler error code. Note that beyond
- // expiration this message format is also
- // used if the key is not yet valid, or
- // has been revoked.
- code: number;
-
- // Signature by the exchange over a
- // `TALER_DenominationExpiredAffirmationPS`.
- // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED``.
- exchange_sig: EddsaSignature;
-
- // Public key of the exchange used to create
- // the 'exchange_sig.
- exchange_pub: EddsaPublicKey;
-
- // Hash of the denomination public key that is unknown.
- h_denom_pub: HashCode;
+ .. ts:def:: BatchWithdrawRequest
- // When was the signature created.
- timestamp: Timestamp;
+ interface BatchWithdrawRequest {
+ // Array of requests for the individual coins to withdraw.
+ planchets: WithdrawRequest[];
- // What kind of operation was requested that now
- // failed?
- oper: string;
}
-
.. ts:def:: WithdrawRequest
interface WithdrawRequest {
- // Hash of a denomination public key (RSA), specifying the type of coin the client
+ // Hash of a denomination public key, specifying the type of coin the client
// would like the exchange to create.
denom_pub_hash: HashCode;
@@ -2149,6 +1806,16 @@ Withdraw
}
+
+ .. ts:def:: BatchWithdrawResponse
+
+ interface BatchWithdrawResponse {
+ // Array of blinded signatures, in the same order as was
+ // given in the request.
+ ev_sigs: WithdrawResponse[];
+
+ }
+
.. ts:def:: WithdrawResponse
interface WithdrawResponse {
@@ -2228,91 +1895,39 @@ Withdraw
history: TransactionHistoryItem[]
}
+ .. ts:def:: DenominationExpiredMessage
+ interface DenominationExpiredMessage {
+ // Taler error code. Note that beyond
+ // expiration this message format is also
+ // used if the key is not yet valid, or
+ // has been revoked.
+ code: number;
-Batch Withdraw
-~~~~~~~~~~~~~~
-
-
-.. http:post:: /reserves/$RESERVE_PUB/batch-withdraw
-
- Withdraw multiple coins from the same reserve. Note that the client should
- commit all of the request details, including the private key of the coins and
- the blinding factors, to disk *before* issuing this request, so that it can
- recover the information if necessary in case of transient failures, like
- power outage, network outage, etc.
-
- **Request:** The request body must be a `BatchWithdrawRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The request was successful, and the response is a `BatchWithdrawResponse`.
- Note that repeating exactly the same request will again yield the same
- response, so if the network goes down during the transaction or before the
- client can commit the coin signature to disk, the coin is not lost.
- :http:statuscode:`403 Forbidden`:
- A signature is invalid.
- This response comes with a standard `ErrorDetail` response.
- :http:statuscode:`404 Not found`:
- A denomination key or the reserve are not known to the exchange. If the
- denomination key is unknown, this suggests a bug in the wallet as the
- wallet should have used current denomination keys from ``/keys``.
- In this case, the response will be a `DenominationUnknownMessage`.
- If the reserve is unknown, the wallet should not report a hard error yet, but
- instead simply wait for up to a day, as the wire transaction might simply
- not yet have completed and might be known to the exchange in the near future.
- In this case, the wallet should repeat the exact same request later again
- using exactly the same blinded coin.
- :http:statuscode:`409 Conflict`:
- The balance of the reserve is not sufficient to withdraw the coins of the
- indicated denominations. The response is `WithdrawError` object.
- :http:statuscode:`410 Gone`:
- A requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked.
- The response is a `DenominationExpiredMessage`. Clients must evaluate the
- error code provided to understand which of the cases this is and handle it
- accordingly.
- :http:statuscode:`451 Unavailable for Legal Reasons`:
- This reserve has received funds from a purse or the amount withdrawn
- exceeds another legal threshold and thus the reserve must
- be upgraded to an account (with KYC) before the withdraw can
- complete. Note that this response does NOT affirm that the
- withdraw will ultimately complete with the requested amount.
- The user should be redirected to the provided location to perform
- the required KYC checks to open the account before withdrawing.
- Afterwards, the request should be repeated.
- The response will be an `KycNeededRedirect` object.
-
- Implementation note: internally, we need to
- distinguish between upgrading the reserve to an
- account (due to P2P payment) and identifying the
- owner of the origin bank account (due to exceeding
- the withdraw amount threshold), as we need to create
- a different payto://-URI for the KYC check depending
- on the case.
-
+ // Signature by the exchange over a
+ // `TALER_DenominationExpiredAffirmationPS`.
+ // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED``.
+ exchange_sig: EddsaSignature;
- **Details:**
+ // Public key of the exchange used to create
+ // the 'exchange_sig.
+ exchange_pub: EddsaPublicKey;
- .. ts:def:: BatchWithdrawRequest
+ // Hash of the denomination public key that is unknown.
+ h_denom_pub: HashCode;
- interface BatchWithdrawRequest {
- // Array of requests for the individual coins to withdraw.
- planchets: WithdrawRequest[];
+ // When was the signature created.
+ timestamp: Timestamp;
+ // What kind of operation was requested that now
+ // failed?
+ oper: string;
}
- .. ts:def:: BatchWithdrawResponse
- interface BatchWithdrawResponse {
- // Array of blinded signatures, in the same order as was
- // given in the request.
- ev_sigs: WithdrawResponse[];
- }
Withdraw with Age Restriction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2326,7 +1941,7 @@ coins along with their age commitments to proof that they were appropriate.
If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
-.. http:POST:: /reserves/$RESERVE_PUB/age-withdraw
+.. http:post:: /reserves/$RESERVE_PUB/age-withdraw
Withdraw multiple coins *with age restriction* from the same reserve.
Note that the client should commit all of the request details, including the
@@ -2347,8 +1962,13 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
A signature is invalid.
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`409 Conflict`:
- The balance of the reserve is not sufficient to withdraw the coins of the
+ One of two reasons occured:
+
+ 1. The balance of the reserve is not sufficient to withdraw the coins of the
given amount. The response is a `WithdrawError` object.
+
+ 2. The provided value for ``max_age`` is higher than the allowed value according to the reserve's birthday.
+ The response comes with a standard `ErrorDetail` response with error-code ``TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE`` and an additional field ``maximum_allowed_age`` for the maximum age (in years) that the client can commit to in a call to ``/age-withdraw``
:http:statuscode:`410 Gone`:
A requested denomination key is not yet or no longer valid.
It either before the validity start, past the expiration or was revoked.
@@ -2369,20 +1989,21 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
.. ts:def:: AgeWithdrawRequest
interface AgeWithdrawRequest {
- // Commitment to the coins with age restriction. This is the SHA512
- // hash value $ACH over all n*kappa `BlindedCoinHash` values of all
- // coins and their age commitments. It is alter used as part of the URL
- // in the subsequent call to /age-withdraw/$ACH/reveal.
- h_commitment : HashCode;
-
- // The total amount that the client wants to withdraw from the reserve
- // and must be at most the balance of the reserve. The balance of the
- // reserve will be immediatley reduced by that amount.
- // In the subsequent call to /age-withdraw/$ACH/reveal, the client has to
- // provide the list of denominations (with support for age restriction)
- // that the coins shall be signed with. The sum of the values of those
- // denominations MUST equal this amount.
- amount: Amount;
+ // Array of ``n`` hash codes of denomination public keys to order.
+ // These denominations MUST support age restriction as defined in the
+ // output to /keys.
+ // The sum of all denomination's values and fees MUST be at most the
+ // balance of the reserve. The balance of the reserve will be
+ // immediatley reduced by that amount.
+ denoms_h: HashCode[];
+
+ // ``n`` arrays of ``kappa`` entries with blinded coin envelopes. Each
+ // (toplevel) entry represents ``kappa`` canditates for a particular
+ // coin. The exchange will respond with an index ``gamma``, which is
+ // the index that shall remain undisclosed during the reveal phase.
+ // The SHA512 hash $ACH over the blinded coin envelopes is the commitment
+ // that is later used as the key to the reveal-URL.
+ blinded_coins_evs: CoinEnvelope[][];
// The maximum age to commit to. MUST be the same as the maximum
// age in the reserve.
@@ -2401,7 +2022,7 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
// have to disclose
noreveal_index: Integer;
- // Signature of `TALER_AgeWithdrawRequestPS` whereby
+ // Signature of `TALER_AgeWithdrawConfirmationPS` whereby
// the exchange confirms the ``noreveal_index``.
exchange_sig: EddsaSignature;
@@ -2414,7 +2035,7 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
-.. http:POST:: /age-withdraw/$ACH/reveal
+.. http:post:: /age-withdraw/$ACH/reveal
The client has previously committed to multiple coins with age restriction
in a call to ``/reserve/$RESERVE_PUB/age-withdraw`` and got a
@@ -2430,7 +2051,7 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
**Response:**
:http:statuscode:`200 OK`:
- The request was successful, and the response is a `RevealResponse`.
+ The request was successful, and the response is a `AgeWithdrawRevealResponse`.
Note that repeating exactly the same request will again yield the same
response, so if the network goes down during the transaction or before the
client can commit the coin signature to disk, the coin is not lost.
@@ -2440,71 +2061,52 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
The reveal operation failed and the response is an `WithdrawError` object.
The error codes indicate one of two cases:
- 1. An age commitment for one of the coins did not fulfill the required
- maximum age requirement of the corresponding reserve. Error code:
+ 1. An age commitment for at least one of the coins did not fulfill the
+ required maximum age requirement of the corresponding reserve.
+ Error code:
``TALER_EC_EXCHANGE_GENERIC_COIN_AGE_REQUIREMENT_FAILURE``.
- 2. The sum of all denominations in the request is not equal to the amount
- that was given in the previous commitment via the call to
- /reserves/$RESERVE_PUB/age-withdraw. Error code:
- ``TALER_EC_EXCHANGE_GENERIC_MISMATCH_OF_AMOUNT_AND_DENOMINATIONS``.
+ 2. The computation of the hash of the commitment with provided input does
+ result in the value $ACH.
+ Error code:
+ ``TALER_EC_EXCHANGE_AGE_WITHDRAW_REVEAL_INVALID_HASH``
.. ts:def:: AgeWithdrawRevealRequest
interface AgeWithdrawRevealRequest {
- // The public key of the reserve that was used for the initial commitment
- // request. Needed for optimized database lookup.
- reserve_pub: EddsaPublicKey;
-
- // Array of ``n`` hash codes of denomination public keys to order.
- // These denominations MUST support age restriction as defined in the
- // output to /keys.
- // The sum of all denomination's values MUST equal the original amount
- // of the previous commitment.
- denoms_h: HashCode[];
-
- // Array of ``n`` entries with blinded coins, which are the non-desclosed
- // coins in the previous commitment. They match the respective entries
- // in ``denoms_h``.
- coin_evs: CoinEnvelope[];
-
- // Array of ``n`` of ``(kappa - 1)`` disclosed coin secrets, from
- // which the coins' private key ``coin_priv``, blinding ``beta`` and nonce
- // ``nonce`` (for Clause-Schnorr) itself are derived as usually in wallet
- // core.
- // (TODO: description of the derivation process of the coin's private
- // key, blinding and nonce).
- //
- // Given a coin's secret, the age commitment for the coin MUST be
- // derived from this private key as follows:
- //
- // Let m ∈ {1,...,M} be the maximum age group as defined in the reserve
- // that the wallet can commit to.
- //
- // For age group $AG ∈ {1,...m}, set
- // seed = HDKF(coin_secret, "age-commitment", $AG)
- // p[$AG] = Edx25519_generate_private(seed)
- // and calculate the corresponding Edx25519PublicKey as
- // q[$AG] = Edx25519_public_from_private(p[$AG])
+ // Array of ``n`` of ``(kappa - 1)`` disclosed coin master secrets, from
+ // which the coins' private key, blinding, nonce (for Clause-Schnorr) and
+ // age-restriction is calculated.
//
- // For age groups $AG ∈ {m,...,M}, set
- // f[$AG] = HDKF(coin_secret, "age-factor", $AG)
- // and calculate the corresponding Edx25519PublicKey as
- // q[$AG] = Edx25519_derive_public(`PublishedAgeRestrictionBaseKey`, f[$AG])
- //
- // Given each coin's private key and age commitment (``q[]``), the
- // exchange will calculate each coin's blinded hash value und use all
- // those (disclosed) blinded hashes together with the non-disclosed
- // envelopes ``coin_evs`` during the verification of the original
- // age-withdraw-commitment.
+ // Given each coin's private key and age commitment, the exchange will
+ // calculate each coin's blinded hash value und use all those (disclosed)
+ // blinded hashes together with the non-disclosed envelopes ``coin_evs``
+ // during the verification of the original age-withdraw-commitment.
disclosed_coin_secrets: AgeRestrictedCoinSecret[][];
-
}
.. ts:def:: AgeRestrictedCoinSecret
- // The Master key material for the derivation of age restricted private
- // coins, blinding factors and age restrictions
+ // The Master key material from which the coins' private key ``coin_priv``,
+ // blinding ``beta`` and nonce ``nonce`` (for Clause-Schnorr) itself are
+ // derived as usually in wallet-core. Given a coin's master key material,
+ // the age commitment for the coin MUST be derived from this private key as
+ // follows:
+ //
+ // Let m ∈ {1,...,M} be the maximum age group as defined in the reserve
+ // that the wallet can commit to.
+ //
+ // For age group $AG ∈ {1,...m}, set
+ // seed = HDKF(coin_secret, "age-commitment", $AG)
+ // p[$AG] = Edx25519_generate_private(seed)
+ // and calculate the corresponding Edx25519PublicKey as
+ // q[$AG] = Edx25519_public_from_private(p[$AG])
+ //
+ // For age groups $AG ∈ {m,...,M}, set
+ // f[$AG] = HDKF(coin_secret, "age-factor", $AG)
+ // and calculate the corresponding Edx25519PublicKey as
+ // q[$AG] = Edx25519_derive_public(`PublishedAgeRestrictionBaseKey`, f[$AG])
+ //
type AgeRestrictedCoinSecret = string;
.. ts:def:: PublishedAgeRestrictionBaseKey
@@ -2515,354 +2117,420 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
// derived all public keys to age groups higher than their allowed maximum
// from this particular value.
const PublishedAgeRestrictionBaseKey =
- new Edx25519PublicKey("DZJRF6HXN520505XDAWM8NMH36QV9J3VH77265WQ09EBQ76QSKCG");
+ new Edx25519PublicKey("CH0VKFDZ2GWRWHQBBGEK9MWV5YDQVJ0RXEE0KYT3NMB69F0R96TG");
+ .. ts:def:: AgeWithdrawRevealResponse
+ interface AgeWithdrawRevealResponse {
+ // List of the exchange's blinded RSA signatures on the new coins.
+ ev_sigs : BlindedDenominationSignature[];
+ }
-.. _deposit-par:
-
--------
-Deposit
--------
-
-Deposit operations are requested f.e. by a merchant during a transaction or a
-bidder during an auction.
-For the deposit operation during purchase, the merchant has to obtain the
-deposit permission for a coin from their customer who owns the coin. When
-depositing a coin, the merchant is credited an amount specified in the deposit
-permission, possibly a fraction of the total coin's value, minus the deposit
-fee as specified by the coin's denomination.
+.. _reserve-history:
-For auctions, a bidder performs an deposit operation and provides all relevant
-information for the auction policy (such as timeout and public key as bidder)
-and can use the ``exchange_sig`` field from the `DepositSuccess` message as a
-proof to the seller for the escrow of sufficient fund.
+---------------
+Reserve History
+---------------
+.. http:get:: /reserves/$RESERVE_PUB/history
-.. _deposit:
+ Request information about the full history of
+ a reserve or an account.
-.. http:POST:: /coins/$COIN_PUB/deposit
+ **Request:**
- Deposit the given coin and ask the exchange to transfer the given :ref:`amount`
- to the merchant's bank account. This API is used by the merchant to redeem
- the digital coins.
+ The GET request should come with the following HTTP headers:
- The base URL for ``/coins/``-requests may differ from the main base URL of the
- exchange. The exchange MUST return a 307 or 308 redirection to the correct
- base URL if this is the case.
+ *If-None-Match*: The client MAY provide an ``If-None-Match`` header with an
+ Etag. In that case, the server MUST additionally respond with an ``304``
+ status code in case the reserve history matches the provided Etag.
- **Request:**
+ *Taler-Reserve-History-Signature*: The client MUST provide Base-32 encoded
+ EdDSA signature over a TALER_SIGNATURE_RESERVE_HISTORY_REQUEST made with
+ the respective ``$RESERVE_PRIV``, affirming desire to download the current
+ reserve transaction history.
- The request body must be a `DepositRequest` object.
+ :query start=OFFSET: *Optional.* Only return reserve history entries with
+ offsets above the given OFFSET. Allows clients to not
+ retrieve history entries they already have.
**Response:**
:http:statuscode:`200 OK`:
- The operation succeeded, the exchange confirms that no double-spending took
- place. The response will include a `DepositSuccess` object.
+ The exchange responds with a `ReserveHistory` object; the reserve was known to the exchange.
+ :http:statuscode:`204 No content`:
+ The reserve history is known, but at this point from the given starting point it is empty. Can only happen if OFFSET was positive.
+ :http:statuscode:`304 Not modified`:
+ The reserve history matches the one identified by the "If-none-match" HTTP header of the request.
:http:statuscode:`403 Forbidden`:
- One of the signatures is invalid.
+ The *TALER_SIGNATURE_RESERVE_HISTORY_REQUEST* is invalid.
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
- Either the denomination key is not recognized (expired or invalid),
- or the wire type is not recognized.
- If the denomination key is unknown, the response will be
- a `DenominationUnknownMessage`.
- :http:statuscode:`409 Conflict`:
- The deposit operation has either failed because the coin has insufficient
- residual value, or because the same public key of the coin has been
- previously used with a different denomination. Which case it is
- can be decided by looking at the error code
- (``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` or
- ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``).
- The fields of the response are the same in both cases.
- The request should not be repeated again with this coin.
- In this case, the response is a `DepositDoubleSpendError`.
- :http:statuscode:`410 Gone`:
- The requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked. The response is a
- `DenominationExpiredMessage`. Clients must evaluate
- the error code provided to understand which of the
- cases this is and handle it accordingly.
+ The reserve key does not belong to a reserve known to the exchange.
**Details:**
- .. ts:def:: DepositRequest
+ .. ts:def:: ReserveHistory
- interface DepositRequest {
- // Amount to be deposited, can be a fraction of the
- // coin's total value.
- contribution: Amount;
+ interface ReserveHistory {
+ // Balance left in the reserve.
+ balance: Amount;
- // The merchant's account details.
- // In case of an auction policy, it refers to the seller.
- merchant_payto_uri: string;
+ // If set, gives the maximum age group that the client is required to set
+ // during withdrawal.
+ maximum_age_group: number;
- // The salt is used to hide the ``payto_uri`` from customers
- // when computing the ``h_wire`` of the merchant.
- wire_salt: WireSalt;
+ // Transaction history for this reserve.
+ // May be partial (!).
+ history: TransactionHistoryItem[];
+ }
- // SHA-512 hash of the contract of the merchant with the customer. Further
- // details are never disclosed to the exchange.
- h_contract_terms: HashCode;
+ Objects in the transaction history have the following format:
- // Hash of denomination RSA key with which the coin is signed.
- denom_pub_hash: HashCode;
+ .. ts:def:: TransactionHistoryItem
- // IFF the corresponding denomination has support for
- // age restriction enabled, this field MUST contain the SHA256
- // value of the age commitment that MUST have been provided during the
- // purchase.
- age_commitment_hash?: AgeCommitmentHash;
+ // Union discriminated by the "type" field.
+ type TransactionHistoryItem =
+ | AccountSetupTransaction
+ | ReserveWithdrawTransaction
+ | ReserveAgeWithdrawTransaction
+ | ReserveCreditTransaction
+ | ReserveClosingTransaction
+ | ReserveOpenRequestTransaction
+ | ReserveCloseRequestTransaction
+ | PurseMergeTransaction;
- // Exchange's unblinded RSA signature of the coin.
- ub_sig: DenominationSignature;
+ .. ts:def:: AccountSetupTransaction
- // Timestamp when the contract was finalized.
- timestamp: Timestamp;
+ interface AccountSetupTransaction {
+ type: "SETUP";
- // Indicative time by which the exchange undertakes to transfer the funds to
- // the merchant, in case of successful payment. A wire transfer deadline of 'never'
- // is not allowed.
- wire_transfer_deadline: Timestamp;
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- // EdDSA `public key of the merchant <merchant-pub>`, so that the client can identify the
- // merchant for refund requests.
- //
- // THIS FIELD WILL BE DEPRICATED, once the refund mechanism becomes a
- // policy via extension.
- merchant_pub: EddsaPublicKey;
+ // KYC fee agreed to by the reserve owner.
+ kyc_fee: Amount;
- // Date until which the merchant can issue a refund to the customer via the
- // exchange, to be omitted if refunds are not allowed.
- //
- // THIS FIELD WILL BE DEPRICATED, once the refund mechanism becomes a
- // policy via extension.
- refund_deadline?: Timestamp;
+ // Time when the KYC was triggered.
+ kyc_timestamp: Timestamp;
- // CAVEAT: THIS IS WORK IN PROGRESS
- // (Optional) policy for the deposit.
- // This might be a refund, auction or escrow policy.
- //
- // Note that support for policies is an optional feature of the exchange.
- // Optional features are so called "extensions" in Taler. The exchange
- // provides the list of supported extensions, including policies, in the
- // `ExtensionsManifestsResponse` response to the ``/keys`` endpoint.
- policy?: DepositPolicy;
+ // Hash of the wire details of the account.
+ // Note that this hash is unsalted and potentially
+ // private (as it could be inverted), hence access
+ // to this endpoint must be authorized using the
+ // private key of the reserve.
+ h_wire: HashCode;
+
+ // Signature created with the reserve's private key.
+ // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST`` over
+ // a ``TALER_AccountSetupRequestSignaturePS``.
+ reserve_sig: EddsaSignature;
- // Signature over `TALER_DepositRequestPS`, made by the customer with the
- // `coin's private key <coin-priv>`.
- coin_sig: EddsaSignature;
}
- .. ts:def:: DenominationSignature
+ .. ts:def:: ReserveWithdrawTransaction
- type DenominationSignature =
- | RsaDenominationSignature
- | CSDenominationSignature;
+ interface ReserveWithdrawTransaction {
+ type: "WITHDRAW";
- .. ts:def:: RsaDenominationSignature
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- interface RsaDenominationSignature {
- cipher: "RSA";
+ // Amount withdrawn.
+ amount: Amount;
- // RSA signature
- rsa_signature: RsaSignature;
- }
+ // Hash of the denomination public key of the coin.
+ h_denom_pub: HashCode;
- .. ts:def:: CSDenominationSignature
+ // Hash of the blinded coin to be signed.
+ h_coin_envelope: HashCode;
- interface CSDenominationSignature {
- type: "CS";
+ // Signature over a `TALER_WithdrawRequestPS`
+ // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW``
+ // created with the reserve's private key.
+ reserve_sig: EddsaSignature;
- // R value component of the signature.
- cs_signature_r: Cs25519Point;
+ // Fee that is charged for withdraw.
+ withdraw_fee: Amount;
+ }
- // s value component of the signature.
- cs_signature_s: Cs25519Scalar:
+ .. ts:def:: ReserveAgeWithdrawTransaction
- }
+ interface ReserveAgeWithdrawTransaction {
+ type: "AGEWITHDRAW";
- .. ts:def:: DepositPolicy
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- type DepositPolicy =
- | PolicyMerchantRefund
- | PolicyBrandtVickreyAuction
- | PolicyEscrowedPayment;
+ // Total Amount withdrawn.
+ amount: Amount;
- .. ts:def:: PolicyMerchantRefund
+ // Commitment of all ``n*kappa`` blinded coins.
+ h_commitment: HashCode;
- // CAVEAT: THIS IS STILL WORK IN PROGRESS.
- // This policy is optional and might not be supported by the exchange.
- // If it does, the exchange MUST show support for this policy in the
- // ``extensions`` field in the response to ``/keys``.
- interface PolicyMerchantRefund {
- type: "merchant_refund";
+ // Signature over a `TALER_AgeWithdrawRequestPS`
+ // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW``
+ // created with the reserve's private key.
+ reserve_sig: EddsaSignature;
- // EdDSA `public key of the merchant <merchant-pub>`, so that the client
- // can identify the merchant for refund requests.
- merchant_pub: EddsaPublicKey;
+ // Fee that is charged for withdraw.
+ withdraw_fee: Amount;
+ }
- // Date until which the merchant can issue a refund to the customer via
- // the ``/extensions/policy_refund``-endpoint of the exchange.
- deadline: Timestamp;
+
+ .. ts:def:: ReserveCreditTransaction
+
+ interface ReserveCreditTransaction {
+ type: "CREDIT";
+
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
+ // Amount deposited.
+ amount: Amount;
+
+ // Sender account ``payto://`` URL.
+ sender_account_url: string;
+
+ // Opaque identifier internal to the exchange that
+ // uniquely identifies the wire transfer that credited the reserve.
+ wire_reference: Integer;
+
+ // Timestamp of the incoming wire transfer.
+ timestamp: Timestamp;
}
- .. ts:def:: PolicyBrandtVickreyAuction
- // CAVEAT: THIS IS STILL WORK IN PROGRESS.
- // This policy is optional and might not be supported by the exchange.
- // If it does, the exchange MUST show support for this policy in the
- // ``extensions`` field in the response to ``/keys``.
- interface PolicyBrandtVickreyAuction {
- type: "brandt_vickrey_auction";
+ .. ts:def:: ReserveClosingTransaction
- // Public key of this bidder.
- //
- // The bidder uses this key to sign the auction information and
- // the messages it sends to the seller during the auction.
- bidder_pub: EddsaPublicKey;
+ interface ReserveClosingTransaction {
+ type: "CLOSING";
- // Hash of the auction terms
- //
- // The hash should be taken over a normalized JSON object of type
- // `BrandtVickreyAuction`.
- h_auction: HashCode;
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- // The amount that this bidder commits to for this auction
- //
- // This amount can be larger than the contribution of a single coin.
- // The bidder can increase funding of this auction policy by using
- // sufficiently many coins during the deposit operation (single or batch)
- // with the same policy.
- commitment: Amount;
+ // Closing balance.
+ amount: Amount;
- // Date until the auction must have been successfully executed and
- // a valid transcript provided to the
- // ``/extensions/policy_brandt_vickrey_auction``-endpoint of the
- // exchange.
- //
- // [If the auction has not been executed by then] OR [has been executed
- // before then, but this bidder did not win], the coin's value doesn't
- // change and the owner can refresh the coin.
- //
- // If this bidder won the auction, the winning price/amount from the
- // outcome will be substracted from the coin and transfered to the
- // merchant's ``payout_uri`` from the deposit request (minus a potential
- // auction fee). For any remaining value, the bidder can refresh the
- // coin to retrieve change.
- deadline: Timestamp;
+ // Closing fee charged by the exchange.
+ closing_fee: Amount;
+
+ // Wire transfer subject.
+ wtid: Base32;
+
+ // ``payto://`` URI of the wire account into which the funds were returned to.
+ receiver_account_details: string;
+
+ // This is a signature over a
+ // struct `TALER_ReserveCloseConfirmationPS` with purpose
+ // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``.
+ exchange_sig: EddsaSignature;
+
+ // Public key used to create 'exchange_sig'.
+ exchange_pub: EddsaPublicKey;
+
+ // Time when the reserve was closed.
+ timestamp: Timestamp;
}
- .. ts:def:: BrandtVickreyAuction
- // CAVEAT: THIS IS STILL WORK IN PROGRESS.
- // This structure defines an auction of Brandt-Vickory kind.
- // It is used for the `PolicyBrandtVickreyAuction`.
- interface BrandtVickreyAuction {
- // Start date of the auction
- time_start: Timestamp;
+ .. ts:def:: ReserveOpenRequestTransaction
- // Maximum duration per round. There are four rounds in an auction of
- // Brandt-Vickrey kind.
- time_round: RelativeTime;
+ interface ReserveOpenRequestTransaction {
+ type: "OPEN";
- // This integer m refers to the (m+1)-type of the Brandt-Vickrey-auction.
- // - Type 0 refers to an auction with one highest-price winner,
- // - Type 1 refers to an auction with one winner, paying the second
- // highest price,
- // - Type 2 refers to an auction with two winners, paying
- // the third-highest price,
- // - etc.
- auction_type: number;
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- // The vector of prices for the Brandt-Vickrey auction. The values MUST
- // be in strictly increasing order.
- prices: Amount[];
+ // Open fee paid from the reserve.
+ open_fee: Amount;
- // The type of outcome of the auction.
- // In case the auction is declared public, each bidder can calculate the
- // winning price. This field is not relevant for the replay of a
- // transcript, as the transcript must be provided by the seller who sees
- // the winner(s) and winning price of the auction.
- outcome_public: boolean;
+ // This is a signature over
+ // a struct `TALER_ReserveOpenPS` with purpose
+ // ``TALER_SIGNATURE_WALLET_RESERVE_OPEN``.
+ reserve_sig: EddsaSignature;
- // The public key of the seller.
- pubkey: EddsaPublicKey;
+ // Timestamp of the open request.
+ request_timestamp: Timestamp;
+
+ // Requested expiration.
+ requested_expiration: Timestamp;
+
+ // Requested number of free open purses.
+ requested_min_purses: Integer;
- // The seller's account details.
- payto_uri: string;
}
+ .. ts:def:: ReserveCloseRequestTransaction
- .. ts:def:: PolicyEscrowedPayment
+ interface ReserveCloseRequestTransaction {
+ type: "CLOSE";
- // CAVEAT: THIS IS STILL WORK IN PROGRESS
- // This policy is optional and might not be supported by the exchange.
- // If it does, the exchange MUST show support for this policy in the
- // ``extensions`` field in the response to ``/keys``.
- interface PolicyEscrowedPayment {
- type: "escrowed_payment";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- // Public key of this trustor, the owner of the coins.
- //
- // To claim the deposit, the merchant must provide the valid signature
- // of the ``h_contract_terms`` field from the deposit, signed by _this_
- // key, to the ``/extensions/policy_escrow``-endpoint of the exchange,
- // after the date specified in ``not_before`` and before the date
- // specified in ``not_after``.
- trustor_pub: EddsaPublicKey;
+ // This is a signature over
+ // a struct `TALER_ReserveClosePS` with purpose
+ // ``TALER_SIGNATURE_WALLET_RESERVE_CLOSE``.
+ reserve_sig: EddsaSignature;
- // Latest date by which the deposit must be claimed. If the deposit
- // has not been claimed by that date, the deposited coins can be
- // refreshed by the (still) owner.
- deadline: Timestamp;
+ // Target account ``payto://``, optional.
+ h_payto?: PaytoHash;
+
+ // Timestamp of the close request.
+ request_timestamp: Timestamp;
}
+ .. ts:def:: ReserveCreditTransaction
- The deposit operation succeeds if the coin is valid for making a deposit and
- has enough residual value that has not already been deposited or melted.
+ interface ReserveCreditTransaction {
+ type: "CREDIT";
- .. ts:def:: DepositSuccess
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- interface DepositSuccess {
- // Optional base URL of the exchange for looking up wire transfers
- // associated with this transaction. If not given,
- // the base URL is the same as the one used for this request.
- // Can be used if the base URL for ``/transactions/`` differs from that
- // for ``/coins/``, i.e. for load balancing. Clients SHOULD
- // respect the ``transaction_base_url`` if provided. Any HTTP server
- // belonging to an exchange MUST generate a 307 or 308 redirection
- // to the correct base URL should a client uses the wrong base
- // URL, or if the base URL has changed since the deposit.
- transaction_base_url?: string;
+ // Amount deposited.
+ amount: Amount;
- // Timestamp when the deposit was received by the exchange.
- exchange_timestamp: Timestamp;
+ // Sender account ``payto://`` URL.
+ sender_account_url: string;
- // The EdDSA signature of `TALER_DepositConfirmationPS` using a current
- // `signing key of the exchange <sign-key-priv>` affirming the successful
- // deposit and that the exchange will transfer the funds after the refund
- // deadline, or as soon as possible if the refund deadline is zero.
- exchange_sig: EddsaSignature;
+ // Opaque identifier internal to the exchange that
+ // uniquely identifies the wire transfer that credited the reserve.
+ wire_reference: Integer;
- // `Public EdDSA key of the exchange <sign-key-pub>` that was used to
- // generate the signature.
- // Should match one of the exchange's signing keys from ``/keys``. It is given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- exchange_pub: EddsaPublicKey;
+ // Timestamp of the incoming wire transfer.
+ timestamp: Timestamp;
}
- .. ts:def:: DepositDoubleSpendError
+ .. ts:def:: PurseMergeTransaction
- interface DepositDoubleSpendError {
- // The string constant "insufficient funds".
- hint: string;
+ interface PurseMergeTransaction {
+ type: "MERGE";
- // Transaction history for the coin that is
- // being double-spended.
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
+ // SHA-512 hash of the contact of the purse.
+ h_contract_terms: HashCode;
+
+ // EdDSA public key used to approve merges of this purse.
+ merge_pub: EddsaPublicKey;
+
+ // Minimum age required for all coins deposited into the purse.
+ min_age: Integer;
+
+ // Number that identifies who created the purse
+ // and how it was paid for.
+ flags: Integer;
+
+ // Purse public key.
+ purse_pub: EddsaPublicKey;
+
+ // EdDSA signature of the account/reserve affirming the merge
+ // over a `TALER_AccountMergeSignaturePS`.
+ // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE``
+ reserve_sig: EddsaSignature;
+
+ // Client-side timestamp of when the merge request was made.
+ merge_timestamp: Timestamp;
+
+ // Indicative time by which the purse should expire
+ // if it has not been merged into an account. At this
+ // point, all of the deposits made should be
+ // auto-refunded.
+ purse_expiration: Timestamp;
+
+ // Purse fee the reserve owner paid for the purse creation.
+ purse_fee: Amount;
+
+ // Total amount merged into the reserve.
+ // (excludes fees).
+ amount: Amount;
+
+ // True if the purse was actually merged.
+ // If false, only the purse_fee has an impact
+ // on the reserve balance!
+ merged: boolean;
+ }
+
+
+.. _coin-history:
+
+------------
+Coin History
+------------
+
+.. http:get:: /coins/$COIN_PUB/history
+
+ Obtain the transaction history of a coin. Used only in special cases, like
+ when the exchange claims a double-spending error and the wallet does not
+ believe it. Usually, the wallet knows the transaction history of each coin
+ and thus has no need to inquire.
+
+ **Request:**
+
+ The GET request should come with the following HTTP headers:
+
+ *If-None-Match*: The client MAY provide an ``If-None-Match`` header with an
+ Etag. In that case, the server MUST additionally respond with an ``304``
+ status code in case the coin history matches the provided Etag.
+
+ *Taler-Coin-History-Signature*: The client MUST provide Base-32 encoded
+ EdDSA signature over a TALER_SIGNATURE_COIN_HISTORY_REQUEST made with
+ the respective ``$RESERVE_PRIV``, affirming desire to download the current
+ coin transaction history.
+
+ :query start=OFFSET: *Optional.* Only return coin history entries with
+ offsets above the given OFFSET. Allows clients to not
+ retrieve history entries they already have.
+
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The operation succeeded, the exchange confirms that no double-spending took
+ place. The response will include a `CoinHistoryResponse` object.
+ :http:statuscode:`204 No content`:
+ The reserve history is known, but at this point from the given starting point it is empty. Can only happen if OFFSET was positive.
+ :http:statuscode:`304 Not modified`:
+ The coin history has not changed since the previous query (detected via Etag
+ in "If-none-match" header).
+ :http:statuscode:`403 Forbidden`:
+ The *TALER_SIGNATURE_COIN_HISTORY_REQUEST* is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The coin public key is not (yet) known to the exchange.
+
+ .. ts:def:: CoinHistoryResponse
+
+ interface CoinHistoryResponse {
+ // Current balance of the coin.
+ balance: Amount;
+
+ // Hash of the coin's denomination.
+ h_denom_pub: HashCode;
+
+ // Transaction history for the coin.
history: CoinSpendHistoryItem[];
}
@@ -2880,12 +2548,16 @@ proof to the seller for the escrow of sufficient fund.
| CoinPurseRefundTransaction
| CoinReserveOpenDepositTransaction;
-
.. ts:def:: CoinDepositTransaction
interface CoinDepositTransaction {
type: "DEPOSIT";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value absorbed (or restored in the
// case of a refund) by this transaction.
// The amount given includes
@@ -2929,6 +2601,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinMeltTransaction {
type: "MELT";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value absorbed by this transaction.
// Note that for melt this means the amount given includes
// the melt fee. The current coin value can thus be computed by
@@ -2958,6 +2635,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinRefundTransaction {
type: "REFUND";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value restored
// by this transaction.
// The amount given excludes the transaction fee.
@@ -2988,6 +2670,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinRecoupTransaction {
type: "RECOUP";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value absorbed
// by this transaction.
// The current coin value can thus be computed by
@@ -3029,6 +2716,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinOldCoinRecoupTransaction {
type: "OLD-COIN-RECOUP";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value restored
// by this transaction.
// The current coin value can thus be computed by
@@ -3053,6 +2745,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinRecoupRefreshTransaction {
type: "RECOUP-REFRESH";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value absorbed
// by this transaction.
// The current coin value can thus be computed by
@@ -3095,6 +2792,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinPurseDepositTransaction {
type: "PURSE-DEPOSIT";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value absorbed
// by this transaction.
// Note that this means the amount given includes
@@ -3129,6 +2831,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinPurseRefundTransaction {
type: "PURSE-REFUND";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value restored
// by this transaction.
// The amount given excludes the refund fee.
@@ -3158,6 +2865,11 @@ proof to the seller for the escrow of sufficient fund.
interface CoinReserveOpenDepositTransaction {
type: "RESERVE-OPEN-DEPOSIT";
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
// The total amount of the coin's value absorbed
// by this transaction.
// Note that this means the amount given includes
@@ -3175,7 +2887,30 @@ proof to the seller for the escrow of sufficient fund.
}
-.. http:POST:: /batch-deposit
+.. _deposit-par:
+
+-------
+Deposit
+-------
+
+Deposit operations are requested f.e. by a merchant during a transaction or a
+bidder during an auction.
+
+For the deposit operation during purchase, the merchant has to obtain the
+deposit permission for a coin from their customer who owns the coin. When
+depositing a coin, the merchant is credited an amount specified in the deposit
+permission, possibly a fraction of the total coin's value, minus the deposit
+fee as specified by the coin's denomination.
+
+For auctions, a bidder performs an deposit operation and provides all relevant
+information for the auction policy (such as timeout and public key as bidder)
+and can use the ``exchange_sig`` field from the `DepositSuccess` message as a
+proof to the seller for the escrow of sufficient fund.
+
+
+.. _deposit:
+
+.. http:post:: /batch-deposit
Deposit multiple coins and ask the exchange to transfer the given :ref:`amount`
into the merchant's bank account. This API is used by the merchant to redeem
@@ -3189,7 +2924,7 @@ proof to the seller for the escrow of sufficient fund.
:http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that no double-spending took
- place. The response will include a `BatchDepositSuccess` object.
+ place. The response will include a `DepositSuccess` object.
:http:statuscode:`403 Forbidden`:
One of the signatures is invalid.
This response comes with a standard `ErrorDetail` response.
@@ -3201,15 +2936,17 @@ proof to the seller for the escrow of sufficient fund.
:http:statuscode:`409 Conflict`:
The deposit operation has either failed because a coin has insufficient
residual value, or because the same public key of a coin has been
- previously used with a different denomination. Which case it is
- can be decided by looking at the error code
- (``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` or
- ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``).
- The fields of the response are the same in both cases.
- The request should not be repeated again with this coin.
- In this case, the response is a `DepositDoubleSpendError` with
- an additional ``coin_pub`` field specifying the public key of the
- coin that was double-spent.
+ previously used with a different denomination.
+ Which case it is can be decided by looking at the error code:
+
+ 1. ``TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT`` (same coin used in different ways),
+ 2. ``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` (balance insufficient),
+ 3. ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY`` (same coin public key, but different denomination).
+ 4. ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH`` (same coin public key, but different age commitment).
+
+ The request should not be repeated again with this coin. Instead, the client
+ can get from the exchange via the ``/coin/$COIN_PUB/history`` endpoint the record
+ of the transactions known for this coin's public key.
:http:statuscode:`410 Gone`:
The requested denomination key is not yet or no longer valid.
It either before the validity start, past the expiration or was revoked. The response is a
@@ -3283,12 +3020,176 @@ proof to the seller for the escrow of sufficient fund.
coin_sig: EddsaSignature;
}
+ .. ts:def:: DenominationSignature
+
+ type DenominationSignature =
+ | RsaDenominationSignature
+ | CSDenominationSignature;
+
+ .. ts:def:: RsaDenominationSignature
+
+ interface RsaDenominationSignature {
+ cipher: "RSA";
+
+ // RSA signature
+ rsa_signature: RsaSignature;
+ }
+
+ .. ts:def:: CSDenominationSignature
+
+ interface CSDenominationSignature {
+ type: "CS";
+
+ // R value component of the signature.
+ cs_signature_r: Cs25519Point;
+
+ // s value component of the signature.
+ cs_signature_s: Cs25519Scalar:
+
+ }
+
+ .. ts:def:: DepositPolicy
+
+ type DepositPolicy =
+ | PolicyMerchantRefund
+ | PolicyBrandtVickreyAuction
+ | PolicyEscrowedPayment;
+
+ .. ts:def:: PolicyMerchantRefund
+
+ // CAVEAT: THIS IS STILL WORK IN PROGRESS.
+ // This policy is optional and might not be supported by the exchange.
+ // If it does, the exchange MUST show support for this policy in the
+ // ``extensions`` field in the response to ``/keys``.
+ interface PolicyMerchantRefund {
+ type: "merchant_refund";
+
+ // EdDSA `public key of the merchant <merchant-pub>`, so that the client
+ // can identify the merchant for refund requests.
+ merchant_pub: EddsaPublicKey;
+
+ // Date until which the merchant can issue a refund to the customer via
+ // the ``/extensions/policy_refund``-endpoint of the exchange.
+ deadline: Timestamp;
+ }
+
+ .. ts:def:: PolicyBrandtVickreyAuction
+
+ // CAVEAT: THIS IS STILL WORK IN PROGRESS.
+ // This policy is optional and might not be supported by the exchange.
+ // If it does, the exchange MUST show support for this policy in the
+ // ``extensions`` field in the response to ``/keys``.
+ interface PolicyBrandtVickreyAuction {
+ type: "brandt_vickrey_auction";
+
+ // Public key of this bidder.
+ //
+ // The bidder uses this key to sign the auction information and
+ // the messages it sends to the seller during the auction.
+ bidder_pub: EddsaPublicKey;
+
+ // Hash of the auction terms
+ //
+ // The hash should be taken over a normalized JSON object of type
+ // `BrandtVickreyAuction`.
+ h_auction: HashCode;
+
+ // The amount that this bidder commits to for this auction
+ //
+ // This amount can be larger than the contribution of a single coin.
+ // The bidder can increase funding of this auction policy by using
+ // sufficiently many coins during the deposit operation (single or batch)
+ // with the same policy.
+ commitment: Amount;
+
+ // Date until the auction must have been successfully executed and
+ // a valid transcript provided to the
+ // ``/extensions/policy_brandt_vickrey_auction``-endpoint of the
+ // exchange.
+ //
+ // [If the auction has not been executed by then] OR [has been executed
+ // before then, but this bidder did not win], the coin's value doesn't
+ // change and the owner can refresh the coin.
+ //
+ // If this bidder won the auction, the winning price/amount from the
+ // outcome will be substracted from the coin and transfered to the
+ // merchant's ``payout_uri`` from the deposit request (minus a potential
+ // auction fee). For any remaining value, the bidder can refresh the
+ // coin to retrieve change.
+ deadline: Timestamp;
+ }
+
+ .. ts:def:: BrandtVickreyAuction
+
+ // CAVEAT: THIS IS STILL WORK IN PROGRESS.
+ // This structure defines an auction of Brandt-Vickory kind.
+ // It is used for the `PolicyBrandtVickreyAuction`.
+ interface BrandtVickreyAuction {
+ // Start date of the auction
+ time_start: Timestamp;
+
+ // Maximum duration per round. There are four rounds in an auction of
+ // Brandt-Vickrey kind.
+ time_round: RelativeTime;
+
+ // This integer m refers to the (m+1)-type of the Brandt-Vickrey-auction.
+ // - Type 0 refers to an auction with one highest-price winner,
+ // - Type 1 refers to an auction with one winner, paying the second
+ // highest price,
+ // - Type 2 refers to an auction with two winners, paying
+ // the third-highest price,
+ // - etc.
+ auction_type: number;
+
+ // The vector of prices for the Brandt-Vickrey auction. The values MUST
+ // be in strictly increasing order.
+ prices: Amount[];
+
+ // The type of outcome of the auction.
+ // In case the auction is declared public, each bidder can calculate the
+ // winning price. This field is not relevant for the replay of a
+ // transcript, as the transcript must be provided by the seller who sees
+ // the winner(s) and winning price of the auction.
+ outcome_public: boolean;
+
+ // The public key of the seller.
+ pubkey: EddsaPublicKey;
+
+ // The seller's account details.
+ payto_uri: string;
+ }
+
+
+ .. ts:def:: PolicyEscrowedPayment
+
+ // CAVEAT: THIS IS STILL WORK IN PROGRESS
+ // This policy is optional and might not be supported by the exchange.
+ // If it does, the exchange MUST show support for this policy in the
+ // ``extensions`` field in the response to ``/keys``.
+ interface PolicyEscrowedPayment {
+ type: "escrowed_payment";
+
+ // Public key of this trustor, the owner of the coins.
+ //
+ // To claim the deposit, the merchant must provide the valid signature
+ // of the ``h_contract_terms`` field from the deposit, signed by _this_
+ // key, to the ``/extensions/policy_escrow``-endpoint of the exchange,
+ // after the date specified in ``not_before`` and before the date
+ // specified in ``not_after``.
+ trustor_pub: EddsaPublicKey;
+
+ // Latest date by which the deposit must be claimed. If the deposit
+ // has not been claimed by that date, the deposited coins can be
+ // refreshed by the (still) owner.
+ deadline: Timestamp;
+ }
+
The deposit operation succeeds if the coin is valid for making a deposit and
has enough residual value that has not already been deposited or melted.
- .. ts:def:: BatchDepositSuccess
+ .. ts:def:: DepositSuccess
- interface BatchDepositSuccess {
+ interface DepositSuccess {
// Optional base URL of the exchange for looking up wire transfers
// associated with this transaction. If not given,
// the base URL is the same as the one used for this request.
@@ -3310,15 +3211,7 @@ proof to the seller for the escrow of sufficient fund.
// which signing key was used.
exchange_pub: EddsaPublicKey;
- // Array of deposit confirmation signatures from the exchange
- // Entries must be in the same order the coins were given
- // in the batch deposit request.
- exchange_sigs: DepositConfirmationSignature[];
- }
-
- .. ts:def:: DepositConfirmationSignature
-
- interface DepositConfirmationSignature {
+ // Deposit confirmation signature from the exchange.
// The EdDSA signature of `TALER_DepositConfirmationPS` using a current
// `signing key of the exchange <sign-key-priv>` affirming the successful
// deposit and that the exchange will transfer the funds after the refund
@@ -3326,6 +3219,27 @@ proof to the seller for the escrow of sufficient fund.
exchange_sig: EddsaSignature;
}
+ .. ts:def:: DepositDoubleSpendError
+
+ interface DepositDoubleSpendError {
+
+ // Must be TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS
+ code: Integer;
+
+ // A string explaining that the user tried to
+ // double-spend.
+ hint: string;
+
+ // EdDSA public key of a coin being double-spent.
+ coin_pub: EddsaPublicKey;
+
+ // Transaction history for the coin that is
+ // being double-spended.
+ // DEPRECATED! Will be removed soon. Use
+ // GET /coins/$COIN_PUB to get the history!
+ history: CoinSpendHistoryItem[];
+ }
+
----------
Refreshing
@@ -3469,7 +3383,7 @@ the API during normal operation.
// Melt commitment. Hash over the various coins to be withdrawn.
// See also ``TALER_refresh_get_commitment()``.
- rc: TALER_RefreshCommitmentP;
+ rc: HashCode;
// Master seed for the Clause-schnorr R-value
// creation. Must match the /csr-melt request.
@@ -3616,10 +3530,7 @@ the API during normal operation.
.. ts:def:: RevealResponse
- interface RevealResponse {
- // List of the exchange's blinded RSA signatures on the new coins.
- ev_sigs : Array<{ ev_sig: BlindedDenominationSignature }>;
- }
+ type RevealResponse = BatchWithdrawResponse;
.. ts:def:: RevealConflictResponse
@@ -3982,7 +3893,13 @@ typically also view the balance.)
**Request:**
- :query merchant_sig: EdDSA signature of the merchant made with purpose ``TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION`` over a ``TALER_DepositTrackPS``, affirming that it is really the merchant who requires obtaining the wire transfer identifier.
+ :query merchant_sig: EdDSA signature of the merchant made with purpose
+ ``TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION`` over a
+ ``TALER_DepositTrackPS``, affirming that it is really the merchant who
+ requires obtaining the wire transfer identifier.
+ :query timeout_ms=NUMBER: *Optional.* If specified, the exchange will wait
+ up to ``NUMBER`` milliseconds for completion of a deposit operation before
+ sending the HTTP response.
**Response:**
@@ -4062,7 +3979,7 @@ typically also view the balance.)
Refunds
-------
-.. http:POST:: /coins/$COIN_PUB/refund
+.. http:post:: /coins/$COIN_PUB/refund
Undo deposit of the given coin, restoring its value.
@@ -4161,8 +4078,8 @@ Refunds
Wallet-to-wallet transfers
--------------------------
-.. http:GET:: /purses/$PURSE_PUB/merge
-.. http:GET:: /purses/$PURSE_PUB/deposit
+.. http:get:: /purses/$PURSE_PUB/merge
+.. http:get:: /purses/$PURSE_PUB/deposit
Obtain information about a purse. Depending on the suffix,
the long-polling (if any) will wait for either a merge or
@@ -4237,7 +4154,7 @@ Wallet-to-wallet transfers
-.. http:POST:: /purses/$PURSE_PUB/create
+.. http:post:: /purses/$PURSE_PUB/create
Create a purse by depositing money into it. First step of a PUSH payment.
@@ -4503,7 +4420,7 @@ Wallet-to-wallet transfers
was already decided.
-.. http:POST:: /purses/$PURSE_PUB/merge
+.. http:post:: /purses/$PURSE_PUB/merge
Merge purse with account, adding the value of the purse into
the account. Endpoint to be used by the receiver of a PUSH payment.
@@ -4609,7 +4526,7 @@ Wallet-to-wallet transfers
-.. http:POST:: /reserves/$RESERVE_PUB/purse
+.. http:post:: /reserves/$RESERVE_PUB/purse
Create purse for an account. First step of a PULL payment.
@@ -4711,7 +4628,7 @@ Wallet-to-wallet transfers
}
-.. http:POST:: /purses/$PURSE_PUB/deposit
+.. http:post:: /purses/$PURSE_PUB/deposit
Deposit money into a purse. Used by the buyer for a PULL payment.
@@ -4847,7 +4764,7 @@ These endpoints are used to manage exchange-to-exchange payments in support of
wallet-to-wallet payments. Only another exchange should access this endpoint.
-.. http:GET:: /wads/$WAD_ID
+.. http:get:: /wads/$WAD_ID
Obtain information about a wad.
@@ -4935,9 +4852,18 @@ wallet-to-wallet payments. Only another exchange should access this endpoint.
KYC status updates
------------------
-.. http:POST:: /kyc-wallet
+This section describes endpoints used to set up, complete and
+inquire about KYC operations performed by an exchange for
+regulatory compliance.
+
+.. http:post:: /kyc-wallet
Setup KYC identification for a wallet. Returns the KYC UUID.
+ This endpoint is used by compliant Taler wallets when they
+ are about to hit the balance threshold and thus need to have
+ the customer provide their personal details to the exchange.
+ The wallet is identified by its long-lived reserve public key
+ (which is used for P2P payments, not for withdrawals).
**Request:**
@@ -4992,17 +4918,20 @@ KYC status updates
}
-.. http:GET:: /kyc-check/$REQUIREMENT_ROW/$H_PAYTO/$USERTYPE
+.. http:get:: /kyc-check/$REQUIREMENT_ROW/$H_PAYTO/$USERTYPE
+
+ Checks the KYC status of a particular payment target and possibly begins the
+ KYC process. This endpoint is used by wallets or merchants that have been
+ told about a KYC requirement and now want to check if the KYC requirement
+ has been fulfilled. Long-polling may be used to instantly observe a change
+ in the KYC requirement status.
- Check or update KYC status of a particular payment target.
- Returns the current KYC status of the account and, if
- negative, returns the URL where the KYC process can be
- initiated. The ``$REQUIREMENT_ROW`` must have been
- returned previously from an exchange API endpoint that
- determined that KYC was needed. The ``$H_PATYO`` must be
- the hash of the payto:// URI of the payment target.
- The ``$USERTYPE`` states whether the entity to perform
- the KYC is an "individual" or "business".
+ Returns the current KYC status of the requirement process and, if negative,
+ returns the URL where the KYC process can be initiated. The
+ ``$REQUIREMENT_ROW`` must have been returned previously from an exchange API
+ endpoint that determined that KYC was needed. The ``$H_PATYO`` must be the
+ hash of the "payto://" URI of the payment target. The ``$USERTYPE`` states
+ whether the entity to perform the KYC is an "individual" or a "business".
**Request:**
@@ -5021,12 +4950,13 @@ KYC status updates
The response will be an `AccountKycStatus` object.
:http:statuscode:`202 Accepted`:
The user should be redirected to the provided location to perform
- the required KYC checks to open the account. Afterwards, the
- ``/kyc-check/`` request should be repeated.
+ the required KYC checks to satisfy the legal requirements. Afterwards, the
+ ``/kyc-check/`` request should be repeated to check whether the
+ user has completed the process.
The response will be an `AccountKycRedirect` object.
:http:statuscode:`204 No content`:
The exchange is not configured to perform KYC and thus
- generally all accounts are simply considered legitimate.
+ the legal requirements are already satisfied.
:http:statuscode:`402 Payment Required`:
The client must pay the KYC fee for the KYC process.
**This is currently not implemented, see #7365.**
@@ -5034,6 +4964,13 @@ KYC status updates
The provided hash does not match the payment target.
:http:statuscode:`404 Not found`:
The payment target is unknown.
+ :http:statuscode:`451 Unavailable for Legal Reasons`:
+ The transaction cannot be completed due to AML rules.
+ Thus, the operation is currently not stuck on KYC, but
+ on exchange staff performing their AML review. The user
+ should be told to wait and/or contact the exchange operator
+ if the situation persists.
+ The response will be a `AccountAmlBlocked` object.
**Details:**
@@ -5057,6 +4994,12 @@ KYC status updates
// public key used to create the signature.
exchange_pub: EddsaPublicKey;
+
+ // Current AML state for the target account. Non-zero
+ // values indicate that the transfer is blocked due to
+ // AML enforcement.
+ aml_status: Integer;
+
}
.. ts:def:: AccountKycRedirect
@@ -5067,6 +5010,22 @@ KYC status updates
// proceed with the KYC process.
kyc_url: string;
+ // Current AML state for the target account. Non-zero
+ // values indicate that the transfer is blocked due to
+ // AML enforcement.
+ aml_status: Integer;
+
+ }
+
+ .. ts:def:: AccountAmlBlocked
+
+ interface AccountAmlBlocked {
+
+ // Current AML state for the target account. Non-zero
+ // values indicate that the transfer is blocked due to
+ // AML enforcement.
+ aml_status: Integer;
+
}
.. ts:def:: KycDetails
@@ -5079,12 +5038,20 @@ KYC status updates
}
-.. http:GET:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO
+.. http:get:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO
- Update KYC status of a particular payment target. Provides
+ Endpoint accessed from the user's browser at the *end* of a
+ KYC process, possibly providing the exchange with additional
+ credentials to obtain the results of the KYC process.
+ Specifically, the URL arguments should provide
information to the exchange that allows it to verify that the
user has completed the KYC process. The details depend on
- the logic, which is selected by the $PROVIDER_SECTION.
+ the logic, which is selected by the "$PROVIDER_SECTION".
+
+ While this is a GET (and thus safe, and idempotent), the operation
+ may actually trigger significant changes in the exchange's state.
+ In particular, it may update the KYC status of a particular
+ payment target.
**Request:**
@@ -5093,16 +5060,20 @@ KYC status updates
If the KYC plugin logic is OAuth 2.0, the query parameters are:
- :query code=CODE : OAuth 2.0 code argument.
+ :query code=CODE: OAuth 2.0 code argument.
:query state=STATE: OAuth 2.0 state argument with the H_PAYTO.
- .. note::
+ .. note::
- Depending on the OAuth variant used, additional
- query parameters may need to be passed here.
+ Depending on the OAuth variant used, additional
+ query parameters may need to be passed here.
**Response:**
+ Given that the response is returned to a user using a browser and **not** to
+ a Taler wallet, the response format is in human-readable HTML and not in
+ machine-readable JSON.
+
:http:statuscode:`302 Found`:
The KYC operation succeeded and the
payment target is now authorized to transact.
@@ -5120,16 +5091,16 @@ KYC status updates
service within a reasonable time period.
-.. http:GET:: /kyc-webhook/$PROVIDER_SECTION/*
-.. http:POST:: /kyc-webhook/$PROVIDER_SECTION/*
-.. http:GET:: /kyc-webhook/$LOGIC/*
-.. http:POST:: /kyc-webhook/$LOGIC/*
+.. http:get:: /kyc-webhook/$PROVIDER_SECTION/*
+.. http:post:: /kyc-webhook/$PROVIDER_SECTION/*
+.. http:get:: /kyc-webhook/$LOGIC/*
+.. http:post:: /kyc-webhook/$LOGIC/*
- Update KYC status of a particular payment target. Provides
- information to the KYC logic of the exchange that allows
- it to verify that the user has completed the KYC process.
- May be a GET or a POST request, depending on $LOGIC or
- $PROVIDER_SECTION.
+ All of the above endpoints can be used to update KYC status of a particular
+ payment target. They provide information to the KYC logic of the exchange
+ that allows it to verify that the user has completed the KYC process. May
+ be a GET or a POST request, depending on the specific "$LOGIC" and/or the
+ "$PROVIDER_SECTION".
**Request:**
@@ -5139,7 +5110,7 @@ KYC status updates
**Response:**
:http:statuscode:`204 No content`:
- The webhook succeeded.
+ The operation succeeded.
:http:statuscode:`404 Not found`:
The specified logic is unknown.
@@ -5149,12 +5120,12 @@ Reserve control
---------------
This section describes the reserve control API which can be used to (1)
-prevent a reserve from expiring (which is useful if the reserve is used for
-tipping), to (2) pay an annual fee to allow a number of purses to be created
-for the respective reserve without paying a purse fee each time, to (3) obtain
-KYC information associated with a reserve to prove the identity of the person
-sending an invoice to the payer, and to (4) close a reserve before it would
-naturally expire and possibly (5) wire the funds to a designated account.
+prevent a reserve from expiring, to (2) pay an annual fee to allow a number of
+purses to be created for the respective reserve without paying a purse fee
+each time, to (3) obtain KYC information associated with a reserve to prove
+the identity of the person sending an invoice to the payer, and to (4) close a
+reserve before it would naturally expire and possibly (5) wire the funds to a
+designated account.
.. note::
@@ -5162,7 +5133,7 @@ naturally expire and possibly (5) wire the funds to a designated account.
.. http:post:: /reserves/$RESERVE_PUB/open
- Request keeping a reserve open for tipping or invoicing.
+ Request keeping a reserve open for invoicing.
**Request:**
@@ -5447,3 +5418,61 @@ naturally expire and possibly (5) wire the funds to a designated account.
wire_amount: Amount;
}
+
+
+.. _delete-reserve:
+
+.. http:DELETE:: /reserves/$RESERVE_PUB
+
+ Forcefully closes a reserve.
+ The request header must contain an *Account-Request-Signature*.
+ Note: this endpoint is not currently implemented!
+
+ **Request:**
+
+ *Account-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$ACCOUNT_PRIV``, affirming its authorization to delete the account. The purpose used MUST be ``TALER_SIGNATURE_RESERVE_CLOSE``.
+
+ :query force=BOOLEAN: *Optional.* If set to 'true' specified, the exchange
+ will delete the account even if there is a balance remaining.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The operation succeeded, the exchange provides details
+ about the account deletion.
+ The response will include a `ReserveDeletedResponse` object.
+ :http:statuscode:`403 Forbidden`:
+ The *Account-Request-Signature* is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The account is unknown to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The account is still has digital cash in it, the associated
+ wire method is ``void`` and the *force* option was not provided.
+ This response comes with a standard `ErrorDetail` response.
+
+ **Details:**
+
+ .. ts:def:: ReserveDeletedResponse
+
+ interface ReserveDeletedResponse {
+
+ // Final balance of the account.
+ closing_amount: Amount;
+
+ // Current time of the exchange, used as part of
+ // what the exchange signs over.
+ close_time: Timestamp;
+
+ // Hash of the wire account into which the remaining
+ // balance will be transferred. Note: may be the
+ // hash over ``payto://void/`, in which case the
+ // balance is forfeit to the profit of the exchange.
+ h_wire: HashCode;
+
+ // This is a signature over a
+ // struct ``TALER_AccountDeleteConfirmationPS`` with purpose
+ // ``TALER_SIGNATURE_EXCHANGE_RESERVE_DELETED``.
+ exchange_sig: EddsaSignature;
+
+ }
diff --git a/core/api-mailbox.rst b/core/api-mailbox.rst
index 33db482d..34d27ded 100644
--- a/core/api-mailbox.rst
+++ b/core/api-mailbox.rst
@@ -29,6 +29,9 @@ for all details not specified in the individual requests.
The `glossary <https://docs.taler.net/glossary.html#glossary>`_
defines all specific terms used in this section.
+.. contents:: Table of Contents
+ :local:
+
.. include:: tos.rst
-------------------------
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index 6c74e004..1dc128bf 100644
--- a/core/api-merchant.rst
+++ b/core/api-merchant.rst
@@ -1,6 +1,6 @@
..
This file is part of GNU TALER.
- Copyright (C) 2014-2023 Taler Systems SA
+ Copyright (C) 2014-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -20,12 +20,12 @@
.. _merchant-api:
-====================
-Merchant Backend API
-====================
+============================
+Merchant Backend RESTful API
+============================
.. contents:: Table of Contents
-
+ :local:
-----------------------
Base URLs and Instances
@@ -36,11 +36,13 @@ This is useful when multiple businesses want to share the same payment
infrastructure.
Merchant backends have one special ``default`` instance. This ``default``
-instance is used when no explicit instance is specified. Despite its name,
-this instance must be created after the installation. In case *no* default
-instance is found - or its credentials got lost -, the administrator can use
-the default instance's rights by resorting on the ``--auth`` command line option,
-or by restarting the service by providing an environment variable called
+instance is used when no explicit instance is specified. Note that using
+``/instances/default/$ANYTHING`` is deprecated and will result in a permanent
+redirect (HTTP status 308) to ``$ANYTHING``. a Despite its name, this instance
+must be created after the installation. In case *no* default instance is
+found - or its credentials got lost -, the administrator can use the default
+instance's rights by resorting on the ``--auth`` command line option, or by
+restarting the service by providing an environment variable called
``TALER_MERCHANT_TOKEN``.
Each instance (default and others) has a base URL. The resources under
@@ -74,10 +76,10 @@ Examples:
https://merchant-backend.example.com/instances/myinst/orders/ABCD
A private endpoint (explicit "default" instance):
- https://merchant-backend.example.com/instances/default/private/orders
+ https://merchant-backend.example.com/private/orders
A public endpoint (explicit "default" instance):
- https://merchant-backend.example.com/instances/default/orders
+ https://merchant-backend.example.com/orders
Endpoints to manage other instances (ONLY for implicit "default" instance):
https://merchant-backend.example.com/management/instances
@@ -99,12 +101,17 @@ Authentication
Each merchant instance has separate authentication settings for the private API resources
of that instance.
-Currently, the API supports two auth methods:
+Currently, the API supports two main authentication methods:
* ``external``: With this method, no checks are done by the merchant backend.
Instead, a reverse proxy / API gateway must do all authentication/authorization checks.
* ``token``: With this method, the client must provide a ``Authorization: Bearer $TOKEN``
- header, where ``$TOKEN`` is a secret authentication token configured for the instance.
+ header, where ``$TOKEN`` is a secret authentication token configured for the instance which must begin with the RFC 8959 prefix.
+
+Additionally, clients can send a **login token** which they may obtain from
+the ``/private/token`` endpoint. Such a login token is valid only for a
+limited period of time and can be used by clients to avoid storing the
+long-term login secrets from an authentication method.
-----------------
Configuration API
@@ -116,6 +123,7 @@ such as the implemented version of the protocol and the currency used.
.. http:get:: /config
Return the protocol version and currency supported by this merchant backend.
+ This specification corresponds to ``current`` protocol being version **13**.
**Response:**
@@ -133,9 +141,53 @@ such as the implemented version of the protocol and the currency used.
// Name of the protocol.
name: "taler-merchant";
- // Currency supported by this backend.
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since **v8**, may become mandatory in the future.
+ implementation?: string;
+
+ // Default (!) currency supported by this backend.
+ // This is the currency that the backend should
+ // suggest by default to the user when entering
+ // amounts. See ``currencies`` for a list of
+ // supported currencies and how to render them.
currency: string;
+ // How services should render currencies supported
+ // by this backend. Maps
+ // currency codes (e.g. "EUR" or "KUDOS") to
+ // the respective currency specification.
+ // All currencies in this map are supported by
+ // the backend. Note that the actual currency
+ // specifications are a *hint* for applications
+ // that would like *advice* on how to render amounts.
+ // Applications *may* ignore the currency specification
+ // if they know how to render currencies that they are
+ // used with.
+ currencies: { currency : CurrencySpecification};
+
+ // Array of exchanges trusted by the merchant.
+ // Since protocol **v6**.
+ exchanges: ExchangeConfigInfo[];
+
+ }
+
+ .. ts:def:: ExchangeConfigInfo
+
+ interface ExchangeConfigInfo {
+
+ // Base URL of the exchange REST API.
+ base_url: string;
+
+ // Currency for which the merchant is configured
+ // to trust the exchange.
+ // May not be the one the exchange actually uses,
+ // but is the only one we would trust this exchange for.
+ currency: string;
+
+ // Offline master public key of the exchange. The
+ // ``/keys`` data must be signed with this public
+ // key for us to trust it.
+ master_pub: EddsaPublicKey;
}
@@ -147,8 +199,7 @@ This section describes (public) endpoints that wallets must be able
to interact with directly (without HTTP-based authentication). These
endpoints are used to process payments (claiming an order, paying
for the order, checking payment/refund status and aborting payments),
-process refunds (checking refund status, obtaining the refund),
-and to pick up tips.
+and to process refunds (checking refund status, obtaining the refund).
Claiming an order
@@ -237,24 +288,26 @@ Making the payment
Either the client request is malformed or some specific processing error
happened that may be the fault of the client as detailed in the JSON body
of the response.
+ This includes the case where the payment is insufficient (sum is below
+ the required total amount, for example because the wallet calculated the
+ fees wrong).
:http:statuscode:`402 Payment required`:
There used to be a sufficient payment, but due to refunds the amount effectively
paid is no longer sufficient. (If the amount is generally insufficient, we
- return "406 Not Acceptable", only if this is because of refunds we return 402.)
+ return "400 Bad Request", only if this is because of refunds we return 402.)
:http:statuscode:`403 Forbidden`:
One of the coin signatures was not valid.
:http:statuscode:`404 Not found`:
The merchant backend could not find the order or the instance and thus cannot process the payment.
- :http:statuscode:`406 Not acceptable`:
- The payment is insufficient (sum is below the required total amount).
- TODO: Should probably change to a different status code in the future as 406 is technically wrong.
:http:statuscode:`408 Request timeout`:
The backend took too long to process the request. Likely the merchant's connection
to the exchange timed out. Try again.
:http:statuscode:`409 Conflict`:
- The exchange rejected the payment because a coin was already spent, or
+ The exchange rejected the payment because a coin was already spent (or
+ used in a different way for the same purchase previously), or
the merchant rejected the payment because the order was already fully paid
- (and then return signatures with refunds). If a coin was already spent,
+ (and then return signatures with refunds). If a coin was already spent
+ (this includes re-using the same coin after a refund),
the response will include the ``exchange_url`` for which the payment failed,
in addition to the response from the exchange to the ``/batch-deposit`` request.
:http:statuscode:`410 Gone`:
@@ -297,6 +350,21 @@ Making the payment
// The coins used to make the payment.
coins: CoinPaySig[];
+ // Index of the chosen sub-contract.
+ // @since protocol **vSUBSCRIBE**
+ choice_index?: Integer;
+
+ // Input tokens required by choice indicated by `choice_index`.
+ // @since protocol **vSUBSCRIBE**
+ tokens: TokenPaySig[];
+
+ // Array of blinded output tokens to be (blindly) signed by the merchant.
+ // @since protocol **vSUBSCRIBE**
+ tokens_evs: TokenEnvelope[];
+
+ // Custom inputs from the wallet for the contract.
+ wallet_data?: Object;
+
// The session for which the payment is made (or replayed).
// Only set for session-based payments.
session_id?: string;
@@ -313,7 +381,7 @@ Making the payment
coin_pub: EddsaPublicKey;
// Signature made by the denomination public key.
- ub_sig: RsaSignature;
+ ub_sig: UnblindedSignature;
// The hash of the denomination public key associated with this coin.
h_denom: HashCode;
@@ -323,6 +391,78 @@ Making the payment
// URL of the exchange this coin was withdrawn from.
exchange_url: string;
+
+ // Signature affirming the posession of the
+ // respective private key proving that the payer
+ // is old enough. Only provided if the paid contract
+ // has an age restriction and the coin is
+ // age-restricted.
+ minimum_age_sig?: EddsaSignature;
+
+ // Age commitment vector of the coin.
+ // Only provided if the paid contract
+ // has an age restriction and the coin is
+ // age-restricted.
+ age_commitment?: Edx25519PublicKey[];
+
+ // Hash over the agge commitment vector of the coin.
+ // Only provided if the paid contract
+ // does NOT have an age restriction and the coin is
+ // age-restricted.
+ h_age_commitment?: AgeCommitmentHash;
+ }
+
+ .. ts:def:: TokenPaySig
+
+ interface TokenPaySig {
+
+ // Signature on ``TALER_DepositRequestPS`` with the public key of the
+ // token being provisioned to the merchant.
+ token_sig: EddsaSignature;
+
+ // Public key of the token being provisioned to the merchant.
+ token_pub: EddsaPublicKey;
+
+ // Unblinded signature made by the token family public key of the merchant.
+ ub_sig: UnblindedSignature;
+
+ // The hash of the token family public key associated with this token.
+ h_denom: HashCode;
+
+ }
+
+ .. ts:def:: TokenEnvelope
+
+ // This type depends on the cipher used to sign token families. This is
+ // configured by the merchant and defined for each token family in the
+ // contract terms.
+ type TokenEnvelope = RSATokenEnvelope | CSTokenEnvelope;
+
+ .. ts:def:: RSATokenEnvelope
+
+ interface RSATokenEnvelope {
+
+ // RSA is used for the blind signature.
+ cipher: "RSA";
+
+ // Blinded signature of the token's `public EdDSA key <eddsa-token-pub>`.
+ rsa_blinded_pub: BlindedRsaSignature;
+
+ }
+
+ .. ts:def:: CSTokenEnvelope
+
+ interface CSTokenEnvelope {
+ // Blind Clause-Schnorr signature scheme is used for the blind signature.
+ // See https://taler.net/papers/cs-thesis.pdf for details.
+ cipher: "CS";
+
+ // Public nonce
+ cs_nonce: string; // Crockford `Base32` encoded
+
+ // Two Curve25519 scalars, each representing a blinded challenge
+ cs_blinded_c0: string; // Crockford `Base32` encoded
+ cs_blinded_c1: string; // Crockford `Base32` encoded
}
Querying payment status
@@ -356,6 +496,7 @@ Querying payment status
merchant backend may return a response immediately.
:query await_refund_obtained=BOOLEAN: *Optional*. If set to "yes", poll for the order's pending refunds to be picked up. ``timeout_ms`` specifies how long we will wait for the refund.
:query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund above the given AMOUNT. ``timeout_ms`` will specify how long we will wait for the refund.
+ :query allow_refunded_for_repurchase: *Optional*. Since protocol **v9** refunded orders are only returned under "already_paid_order_id" if this flag is set explicitly to "YES".
**Response:**
@@ -455,10 +596,10 @@ again.
**Response:**
- :http:statuscode:`204 No content`:
+ :http:statuscode:`200 Ok`:
The merchant accepted the signature.
The ``frontend`` should now fulfill the contract.
- Note that it is possible that refunds have been granted.
+ Note that it is possible that refunds have been granted. Response is of type `PaidRefundStatusResponse`.
:http:statuscode:`400 Bad request`:
Either the client request is malformed or some specific processing error
happened that may be the fault of the client as detailed in the JSON body
@@ -469,6 +610,21 @@ again.
The merchant backend could not find the order or the instance
and thus cannot process the request.
+ **Details**:
+
+ .. ts:def:: PaidRefundStatusResponse
+
+ interface PaidRefundStatusResponse {
+
+ // Text to be shown to the point-of-sale staff as a proof of
+ // payment (present only if re-usable OTP algorithm is used).
+ pos_confirmation?: string;
+
+ // True if the order has been subjected to
+ // refunds. False if it was simply paid.
+ refunded: boolean;
+ }
+
.. ts:def:: PaidRequest
interface PaidRequest {
@@ -481,6 +637,9 @@ again.
// database access).
h_contract: HashCode;
+ // Hash over custom inputs from the wallet for the contract.
+ wallet_data_hash?: HashCode;
+
// Session id for which the payment is proven.
session_id: string;
}
@@ -517,7 +676,7 @@ are for incomplete payments for an order and never for established contracts.
:http:statuscode:`200 OK`:
The merchant accepted the request, and passed it on to the exchange. The body is a
- a `merchant refund response <MerchantRefundResponse>`. Note that the exchange
+ a `abort response <AbortResponse>`. Note that the exchange
MAY still have encountered errors in processing. Those will then be part of
the body. Wallets MUST carefully consider errors for each of the coins as
returned by the exchange.
@@ -717,6 +876,10 @@ the contract. Refunds must be approved by the merchant's business logic.
// Amount that was refunded, including refund fee charged by the exchange
// to the customer.
refund_amount: Amount;
+
+ // Timestamp when the merchant approved the refund.
+ // Useful for grouping refunds.
+ execution_time: Timestamp;
}
.. ts:def:: MerchantCoinRefundSuccessStatus
@@ -751,111 +914,10 @@ the contract. Refunds must be approved by the merchant's business logic.
// Amount that was refunded, including refund fee charged by the exchange
// to the customer.
refund_amount: Amount;
- }
-
-Picking up tips
----------------
-
-Tips are a way for wallets to obtain e-cash from
-a website.
-
-.. http:get:: [/instances/$INSTANCE]/tips/$TIP_ID
-
- Handle request from wallet to provide details about a tip.
-
- This endpoint typically also supports requests with the "Accept" header
- requesting "text/html". In this case, an HTML response suitable for
- triggering the interaction with the wallet is returned. If the backend
- installation does not include the required HTML templates, a 406 status
- code is returned.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- A tip is being returned. The backend responds with a `TipInformation`.
- :http:statuscode:`404 Not found`:
- The tip identifier is unknown.
- :http:statuscode:`406 Not acceptable`:
- The merchant backend could not load the template required to generate a reply in the desired format. (Likely HTML templates were not properly installed.)
- :http:statuscode:`410 Gone`:
- A tip has been fully claimed. The JSON reply still contains the `TipInformation`.
-
- .. ts:def:: TipInformation
-
- interface TipInformation {
-
- // Exchange from which the tip will be withdrawn. Needed by the
- // wallet to determine denominations, fees, etc.
- exchange_url: string;
-
- // URL where to go after obtaining the tip.
- next_url: string;
-
- // (Remaining) amount of the tip (including fees).
- tip_amount: Amount;
-
- // Timestamp indicating when the tip is set to expire (may be in the past).
- // Note that tips that have expired MAY also result in a 404 response.
- expiration: Timestamp;
- }
-
-
-.. http:post:: [/instances/$INSTANCE]/tips/$TIP_ID/pickup
-
- Handle request from wallet to pick up a tip.
-
- **Request:**
-
- The request body is a `TipPickupRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- A tip is being returned. The backend responds with a `TipResponse`.
- :http:statuscode:`401 Unauthorized`:
- The tip amount requested exceeds the tip.
- :http:statuscode:`404 Not found`:
- The tip identifier is unknown.
- :http:statuscode:`409 Conflict`:
- Some of the denomination key hashes of the request do not match those currently available from the exchange (hence there is a conflict between what the wallet requests and what the merchant believes the exchange can provide).
- :http:statuscode:`410 Gone`:
- The tip has expired.
-
- .. ts:def:: TipPickupRequest
-
- interface TipPickupRequest {
-
- // List of planchets the wallet wants to use for the tip.
- planchets: PlanchetDetail[];
- }
-
- .. ts:def:: PlanchetDetail
-
- interface PlanchetDetail {
- // Hash of the denomination's public key (hashed to reduce
- // bandwidth consumption).
- denom_pub_hash: HashCode;
-
- // Coin's blinded public key.
- coin_ev: CoinEnvelope;
- }
-
- .. ts:def:: TipResponse
-
- interface TipResponse {
-
- // Blind RSA signatures over the planchets.
- // The order of the signatures matches the planchets list.
- blind_sigs: BlindSignature[];
- }
-
- .. ts:def:: BlindSignature
-
- interface BlindSignature {
-
- // The (blind) RSA signature. Still needs to be unblinded.
- blind_sig: BlindedRsaSignature;
+ // Timestamp when the merchant approved the refund.
+ // Useful for grouping refunds.
+ execution_time: Timestamp;
}
@@ -895,13 +957,6 @@ Setting up instances
.. ts:def:: InstanceConfigurationMessage
interface InstanceConfigurationMessage {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array. Note that by
- // removing URIs from this list the respective account is set to
- // inactive and thus unavailable for new contracts, but preserved
- // in the database as existing offers and contracts may still refer
- // to it.
- payto_uris: string[];
// Name of the merchant instance to create (will become $INSTANCE).
// Must match the regex ``^[A-Za-z0-9][A-Za-z0-9_.@-]+$``.
@@ -910,6 +965,11 @@ Setting up instances
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user (business or individual).
+ // Defaults to 'business'. Should become mandatory field
+ // in the future, left as optional for API compatibility for now.
+ user_type?: string;
+
// Merchant email for customer contact.
email?: string;
@@ -929,17 +989,10 @@ Setting up instances
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -953,16 +1006,15 @@ Setting up instances
}
-
.. http:post:: /management/instances/$INSTANCE/auth
.. http:post:: [/instances/$INSTANCE]/private/auth
- Update the authentication settings for an instance. POST operations against
- an instance are authenticated by checking that an authorization is provided
- that matches either the credential required by the instance being modified
- OR the ``default`` instance, depending on the access path used.
+ Update the authentication settings for an instance. POST operations against
+ an instance are authenticated by checking that an authorization is provided
+ that matches either the credential required by the instance being modified
+ OR the ``default`` instance, depending on the access path used.
- **Request** the request must be an `InstanceAuthConfigurationMessage`.
+ **Request** the request must be an `InstanceAuthConfigurationMessage`.
:http:statuscode:`204 No content`:
The backend has successfully created the instance.
@@ -980,7 +1032,7 @@ Setting up instances
// See "token" for details.
method: "external" | "token";
- // For method "external", this field is mandatory.
+ // For method "token", this field is mandatory.
// The token MUST begin with the string "secret-token:".
// After the auth token has been set (with method "token"),
// the value must be provided in a "Authorization: Bearer $token"
@@ -989,6 +1041,62 @@ Setting up instances
}
+.. http:post:: [/instances/$INSTANCE]/private/token
+
+ **Request:**
+
+ The request must be a `LoginTokenRequest`.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The backend is returning the access token in a
+ `LoginTokenSuccessResponse`.
+
+ **Details:**
+
+ .. ts:def:: LoginTokenRequest
+
+ interface LoginTokenRequest {
+ // Scope of the token (which kinds of operations it will allow)
+ scope: "readonly" | "write";
+
+ // Server may impose its own upper bound
+ // on the token validity duration
+ duration?: RelativeTime;
+
+ // Can this token be refreshed?
+ // Defaults to false.
+ refreshable?: boolean;
+ }
+
+ .. ts:def:: LoginTokenSuccessResponse
+
+ interface LoginTokenSuccessResponse {
+ // The login token that can be used to access resources
+ // that are in scope for some time. Must be prefixed
+ // with "Bearer " when used in the "Authorization" HTTP header.
+ // Will already begin with the RFC 8959 prefix.
+ token: string;
+
+ // Scope of the token (which kinds of operations it will allow)
+ scope: "readonly" | "write";
+
+ // Server may impose its own upper bound
+ // on the token validity duration
+ expiration: Timestamp;
+
+ // Can this token be refreshed?
+ refreshable: boolean;
+ }
+
+.. http:delete:: [/instances/$INSTANCE]/private/token
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The access token used to authorize this request was revoked.
+
.. http:patch:: /management/instances/$INSTANCE
.. http:patch:: [/instances/$INSTANCE]/private
@@ -1014,15 +1122,15 @@ Setting up instances
.. ts:def:: InstanceReconfigurationMessage
interface InstanceReconfigurationMessage {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array. Note that removing
- // URIs from this list deactivates the specified accounts
- // (they will no longer be used for future contracts).
- payto_uris: string[];
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user (business or individual).
+ // Defaults to 'business'. Should become mandatory field
+ // in the future, left as optional for API compatibility for now.
+ user_type?: string;
+
// Merchant email for customer contact.
email?: string;
@@ -1039,17 +1147,10 @@ Setting up instances
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -1097,6 +1198,9 @@ Inspecting instances
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user ("business" or "individual").
+ user_type: string;
+
// Merchant public website.
website?: string;
@@ -1136,13 +1240,13 @@ Inspecting instances
.. ts:def:: QueryInstancesResponse
interface QueryInstancesResponse {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array.
- accounts: MerchantAccount[];
// Merchant name corresponding to this instance.
name: string;
+ // Type of the user ("business" or "individual").
+ user_type: string;
+
// Merchant email for customer contact.
email?: string;
@@ -1162,17 +1266,10 @@ Inspecting instances
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -1192,24 +1289,6 @@ Inspecting instances
}
- .. ts:def:: MerchantAccount
-
- interface MerchantAccount {
-
- // payto:// URI of the account.
- payto_uri: string;
-
- // Hash over the wire details (including over the salt).
- h_wire: HashCode;
-
- // Salt used to compute h_wire.
- salt: HashCode;
-
- // true if this account is active,
- // false if it is historic.
- active: boolean;
- }
-
Deleting instances
------------------
@@ -1321,7 +1400,12 @@ KYC status checks
// URL that the user should open in a browser to
// proceed with the KYC process (as returned
// by the exchange's ``/kyc-check/`` endpoint).
- kyc_url: string;
+ // Optional, missing if the account is blocked
+ // due to AML and not due to KYC.
+ kyc_url?: string;
+
+ // AML status of the account.
+ aml_status: Integer;
// Base URL of the exchange this is about.
exchange_url: string;
@@ -1349,6 +1433,193 @@ KYC status checks
}
+
+-------------
+Bank Accounts
+-------------
+
+One or more bank accounts must be associated with an instance
+so that the instance can receive payments. Payments may be made
+into any of the active bank accounts of an instance.
+
+
+.. http:post:: [/instances/$INSTANCE]/private/accounts
+
+ This is used to add an account to an instance.
+
+ **Request:**
+
+ The request must have an `AccountAddDetails` body.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ Adding the account was successful, we return the salt selected by the backend and the resulting wire hash in an `AccountAddResponse`.
+ :http:statuscode:`404 Not found`:
+ The merchant instance is unknown or it is not in our data.
+ :http:statuscode:`409 Conflict`:
+ The provided information is inconsistent with the current state of the instance.
+ Usually this means we already have this account, but with conflicting credit facade information.
+ Inactive accounts can be reactivated using this method even if the
+ credit facade information differs from the previous state.
+
+ .. ts:def:: AccountAddDetails
+
+ interface AccountAddDetails {
+
+ // payto:// URI of the account.
+ payto_uri: string;
+
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
+
+ // Credentials to use when accessing the credit facade.
+ // Never returned on a GET (as this may be somewhat
+ // sensitive data). Can be set in POST
+ // or PATCH requests to update (or delete) credentials.
+ // To really delete credentials, set them to the type: "none".
+ credit_facade_credentials?: FacadeCredentials;
+
+ }
+
+ .. ts:def:: FacadeCredentials
+
+ type FacadeCredentials =
+ | NoFacadeCredentials
+ | BasicAuthFacadeCredentials;
+
+ .. ts:def:: NoFacadeCredentials
+
+ interface NoFacadeCredentials {
+ type: "none";
+ };
+
+ .. ts:def:: BasicAuthFacadeCredentials
+
+ interface BasicAuthFacadeCredentials {
+ type: "basic";
+
+ // Username to use to authenticate
+ username: string;
+
+ // Password to use to authenticate
+ password: string;
+ };
+
+ .. ts:def:: AccountAddResponse
+
+ interface AccountAddResponse {
+
+ // Hash over the wire details (including over the salt).
+ h_wire: HashCode;
+
+ // Salt used to compute h_wire.
+ salt: HashCode;
+
+ }
+
+
+.. http:patch:: [/instances/$INSTANCE]/private/accounts/$H_WIRE
+
+ This is used to update a bank account.
+
+ **Request:**
+
+ The request must be a `AccountPatchDetails`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The template has successfully modified.
+ :http:statuscode:`404 Not found`:
+ The template(ID) is unknown to the backend.
+
+ .. ts:def:: AccountPatchDetails
+
+ interface AccountPatchDetails {
+
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
+
+ // Credentials to use when accessing the credit facade.
+ // Never returned on a GET (as this may be somewhat
+ // sensitive data). Can be set in POST
+ // or PATCH requests to update (or delete) credentials.
+ // To really delete credentials, set them to the type: "none".
+ // If the argument is omitted, the old credentials
+ // are simply preserved.
+ credit_facade_credentials?: FacadeCredentials;
+ }
+
+
+.. http:get:: [/instances/$INSTANCE]/private/accounts
+
+ This is used to return the list of all the bank accounts
+ of an instance.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned all the accounts. Returns a `AccountsSummaryResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: AccountsSummaryResponse
+
+ interface AccountsSummaryResponse {
+
+ // List of accounts that are known for the instance.
+ accounts: BankAccountEntry[];
+ }
+
+ .. ts:def:: BankAccountEntry
+
+ interface BankAccountEntry {
+
+ // payto:// URI of the account.
+ payto_uri: string;
+
+ // Hash over the wire details (including over the salt).
+ h_wire: HashCode;
+
+ // Salt used to compute h_wire.
+ salt: HashCode;
+
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
+
+ // true if this account is active,
+ // false if it is historic.
+ active: boolean;
+ }
+
+.. http:get:: [/instances/$INSTANCE]/private/accounts/$H_WIRE
+
+ This is used to obtain detailed information about a specific bank account.
+
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the detailed information about a specific bank account.
+ Returns a `BankAccountEntry`.
+ :http:statuscode:`404 Not found`:
+ The bank account or instance is unknown to the backend.
+
+
+.. http:delete:: [/instances/$INSTANCE]/private/accounts/$H_WIRE
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deactivated the account.
+ :http:statuscode:`404 Not found`:
+ The backend does not know the instance or the account.
+
+
--------------------
Inventory management
--------------------
@@ -1509,6 +1780,11 @@ Inspecting inventory
This is used to return the list of all items in the inventory.
+ **Request:**
+
+ :query limit: *Optional*. At most return the given number of results. Negative for descending by row ID, positive for ascending by row ID. Default is ``20``. Since protocol **v12**.
+ :query offset: *Optional*. Starting ``product_serial_id`` for an iteration. Since protocol **v12**.
+
**Response:**
:http:statuscode:`200 OK`:
@@ -1532,6 +1808,8 @@ Inspecting inventory
// Product identifier, as found in the product.
product_id: string;
+ // ``product_serial_id`` of the product in the database.
+ product_serial: integer;
}
@@ -1718,6 +1996,7 @@ Creating orders
(2) The merchant instance is unknown (including possibly the instance
being not configured for new orders).
(3) The wire method specified is not supported by the backend.
+ (4) An OTP device ID was specified and is unknown.
Details in the error code.
NOTE: currently the client has no good way to find out which product
@@ -1747,6 +2026,11 @@ Creating orders
// to select among the various (active) wire methods supported by the instance.
payment_target?: string;
+ // The session for which the payment is made (or replayed).
+ // Only set for session-based payments.
+ // Since protocol **v6**.
+ session_id?: string;
+
// Specifies that some products are to be included in the
// order from the inventory. For these inventory management
// is performed (so the products must be in stock) and
@@ -1761,7 +2045,7 @@ Creating orders
// be used in case different UUIDs were used for different
// products (i.e. in case the user started with multiple
// shopping sessions that were combined during checkout).
- lock_uuids: string[];
+ lock_uuids?: string[];
// Should a token for claiming the order be generated?
// False can make sense if the ORDER_ID is sufficiently
@@ -1769,31 +2053,109 @@ Creating orders
// if the backend auto-generates one). Default is 'true'.
create_token?: boolean;
+ // OTP device ID to associate with the order.
+ // This parameter is optional.
+ otp_id?: string;
}
.. ts:def:: Order
- type Order : MinimalOrderDetail | ContractTerms;
+ interface Order {
+ // Total price for the transaction.
+ // The exchange will subtract deposit fees from that amount
+ // before transferring it to the merchant.
+ amount: Amount;
- The following fields must be specified in the ``order`` field of the request. Other fields from
- `ContractTerms` are optional, and will override the defaults in the merchant configuration.
+ // Human-readable description of the whole purchase.
+ summary: string;
- .. ts:def:: MinimalOrderDetail
+ // Map from IETF BCP 47 language tags to localized summaries.
+ summary_i18n?: { [lang_tag: string]: string };
- interface MinimalOrderDetail {
- // Amount to be paid by the customer.
- amount: Amount;
+ // Unique, free-form identifier for the order.
+ // Must be unique within a merchant instance.
+ // For merchants that do not store proposals in their DB
+ // before the customer paid for them, the ``order_id`` can be used
+ // by the frontend to restore a proposal from the information
+ // encoded in it (such as a short product identifier and timestamp).
+ order_id?: string;
- // Short summary of the order.
- summary: string;
+ // List of contract choices that the customer can select from.
+ // @since protocol **vSUBSCRIBE**
+ choices?: ContractChoice[];
+
+ // URL where the same contract could be ordered again (if
+ // available). Returned also at the public order endpoint
+ // for people other than the actual buyer (hence public,
+ // in case order IDs are guessable).
+ public_reorder_url?: string;
- // See documentation of fulfillment_url in ContractTerms.
+ // See documentation of ``fulfillment_url`` field in ``ContractTerms``.
// Either fulfillment_url or fulfillment_message must be specified.
+ // When creating an order, the fulfillment URL can
+ // contain ``${ORDER_ID}`` which will be substituted with the
+ // order ID of the newly created order.
fulfillment_url?: string;
- // See documentation of fulfillment_message in ContractTerms.
+ // See documentation of fulfillment_message in `ContractTerms`.
// Either fulfillment_url or fulfillment_message must be specified.
fulfillment_message?: string;
+
+ // Map from IETF BCP 47 language tags to localized fulfillment
+ // messages.
+ fulfillment_message_i18n?: { [lang_tag: string]: string };
+
+ // Maximum total deposit fee accepted by the merchant for this contract.
+ // Overrides defaults of the merchant instance.
+ max_fee?: Amount;
+
+ // List of products that are part of the purchase.
+ products?: Product[];
+
+ // Time when this contract was generated. If null, defaults to current
+ // time of merchant backend.
+ timestamp?: Timestamp;
+
+ // After this deadline has passed, no refunds will be accepted.
+ // Overrides deadline calculated from ``refund_delay`` in
+ // ``PostOrderRequest``.
+ refund_deadline?: Timestamp;
+
+ // After this deadline, the merchant won't accept payments for the contract.
+ // Overrides deadline calculated from default pay delay configured in
+ // merchant backend.
+ pay_deadline?: Timestamp;
+
+ // Transfer deadline for the exchange. Must be in the deposit permissions
+ // of coins used to pay for this order.
+ // Overrides deadline calculated from default wire transfer delay
+ // configured in merchant backend. Must be after refund deadline.
+ wire_transfer_deadline?: Timestamp;
+
+ // Base URL of the (public!) merchant backend API.
+ // Must be an absolute URL that ends with a slash.
+ // Defaults to the base URL this request was made to.
+ merchant_base_url?: string;
+
+ // Delivery location for (all!) products.
+ delivery_location?: Location;
+
+ // Time indicating when the order should be delivered.
+ // May be overwritten by individual products.
+ // Must be in the future.
+ delivery_date?: Timestamp;
+
+ // See documentation of ``auto_refund`` in ``ContractTerms``.
+ // Specifies for how long the wallet should try to get an
+ // automatic refund for the purchase.
+ auto_refund?: RelativeTime;
+
+ // Extra data that is only interpreted by the merchant frontend.
+ // Useful when the merchant needs to store extra information on a
+ // contract without storing it separately in their database.
+ // Must really be an Object (not a string, integer, float or array).
+ extra?: Object;
+
}
The following `MinimalInventoryProduct` can be provided if the parts of the
@@ -1867,10 +2229,14 @@ Inspecting orders
:query paid: *Optional*. If set to yes, only return paid orders, if no only unpaid orders. Do not give (or use "all") to see all orders regardless of payment status.
:query refunded: *Optional*. If set to yes, only return refunded orders, if no only unrefunded orders. Do not give (or use "all") to see all orders regardless of refund status.
:query wired: *Optional*. If set to yes, only return wired orders, if no only orders with missing wire transfers. Do not give (or use "all") to see all orders regardless of wire transfer status.
- :query delta: *Optional*. takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` and ``date_s`` are returned. Defaults to ``-20`` to return the last 20 entries (before ``start`` and/or ``date_s``).
+ :query delta: *Optional*. takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` and ``date_s`` are returned. Defaults to ``-20`` to return the last 20 entries (before ``start`` and/or ``date_s``). Deprecated in protocol **v12**. Use *limit* instead.
+ :query limit: *Optional*. At most return the given number of results. Negative for descending by row ID, positive for ascending by row ID. Default is ``20``. Since protocol **v12**.
:query date_s: *Optional.* Non-negative date in seconds after the UNIX Epoc, see ``delta`` for its interpretation. If not specified, we default to the oldest or most recent entry, depending on ``delta``.
- :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation. Defaults to ``INT64_MAX``, namely the biggest row id possible in the database.
+ :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation. Defaults to ``INT64_MAX``, namely the biggest row id possible in the database. Deprecated in protocol **v12**. Use *offset* instead.
+ :query offset: *Optional*. Starting ``product_serial_id`` for an iteration. Since protocol **v12**.
:query timeout_ms: *Optional*. Timeout in milliseconds to wait for additional orders if the answer would otherwise be negative (long polling). Only useful if delta is positive. Note that the merchant MAY still return a response that contains fewer than ``delta`` orders.
+ :query session_id: *Optional*. Since protocol **v6**. Filters by session ID.
+ :query fulfillment_url: *Optional*. Since protocol **v6**. Filters by fulfillment URL.
**Response:**
@@ -1927,8 +2293,9 @@ Inspecting orders
**Request:**
:query session_id: *Optional*. Session ID that the payment must be bound to. If not specified, the payment is not session-bound.
- :query transfer: *Optional*. If set to "YES", try to obtain the wire transfer status for this order from the exchange. Otherwise, the wire transfer status MAY be returned if it is available.
+ :query transfer: Deprecated in protocol **V6**. *Optional*. If set to "YES", try to obtain the wire transfer status for this order from the exchange. Otherwise, the wire transfer status MAY be returned if it is available.
:query timeout_ms: *Optional*. Timeout in milliseconds to wait for a payment if the answer would otherwise be negative (long polling).
+ :query allow_refunded_for_repurchase: *Optional*. Since protocol **v9** refunded orders are only returned under "already_paid_order_id" if this flag is set explicitly to "YES".
**Response:**
@@ -1991,6 +2358,7 @@ Inspecting orders
// Reports about trouble obtaining wire transfer details,
// empty array if no trouble were encountered.
+ // @deprecated in protocol **v6**.
wire_reports: TransactionWireReport[];
// The refund details for this order. One entry per
@@ -2265,35 +2633,12 @@ Informing the backend about incoming wire transfers
**Response:**
- :http:statuscode:`200 OK`:
- The wire transfer is known to the exchange, details about it follow in the body.
- The body of the response is a `MerchantTrackTransferResponse`.
- :http:statuscode:`202 Accepted`:
- The exchange provided conflicting information about the transfer. Namely,
- there is at least one deposit among the deposits aggregated by ``wtid``
- that accounts for a coin whose
- details don't match the details stored in merchant's database about the same keyed coin.
- The response body contains the `ExchangeConflictDetails`.
- This is indicative of a malicious exchange that claims one thing, but did
- something else. (With respect to the HTTP specficiation, it is not
- precisely that we did not act upon the request, more that the usual
- action of filing the transaction as 'finished' does not apply. In
- the future, this is a case where the backend actually should report
- the bad behavior to the auditor -- and then hope for the auditor to
- resolve it. So in that respect, 202 is the right status code as more
- work remains to be done for a final resolution.)
+ :http:statuscode:`204 No content`:
+ The wire transfer is now confirmed at the merchant.
:http:statuscode:`404 Not found`:
The instance or account are unknown to the exchange.
:http:statuscode:`409 Conflict`:
- The wire transfer identifier is already known to us, but for a different amount,
- wire method or exchange, or the amount reported by the exchange differs from
- the amount reported by the merchant.
- :http:statuscode:`502 Bad gateway`:
- The exchange returned an error when we asked it about the ``GET /transfer`` status
- for this wire transfer. Details of the exchange error are returned.
- :http:statuscode:`504 Gateway timeout`:
- The merchant's interaction with the exchange took too long.
- The client might want to try again later.
+ The wire transfer identifier is already known to us, but for a different amount.
**Details:**
@@ -2313,168 +2658,6 @@ Informing the backend about incoming wire transfers
exchange_url: string;
}
- .. ts:def:: MerchantTrackTransferResponse
-
- interface MerchantTrackTransferResponse {
- // Total amount transferred.
- total: Amount;
-
- // Applicable wire fee that was charged.
- wire_fee: Amount;
-
- // Time of the execution of the wire transfer by the exchange, according to the exchange.
- execution_time: Timestamp;
-
- // Details about the deposits.
- deposits_sums: MerchantTrackTransferDetail[];
- }
-
- .. ts:def:: MerchantTrackTransferDetail
-
- interface MerchantTrackTransferDetail {
- // Business activity associated with the wire transferred amount
- // ``deposit_value``.
- order_id: string;
-
- // The total amount the exchange paid back for ``order_id``.
- deposit_value: Amount;
-
- // Applicable fees for the deposit.
- deposit_fee: Amount;
- }
-
-
- .. ts:def:: ExchangeConflictDetails
-
- type ExchangeConflictDetails = WireFeeConflictDetails |
- TrackTransferConflictDetails;
-
-
- .. ts:def:: WireFeeConflictDetails
-
- // Note: this is not the full 'proof' of misbehavior, as
- // the bogus message from the exchange with a signature
- // over the 'different' wire fee is missing.
- //
- // This information is NOT provided by the current implementation,
- // because this would be quite expensive to generate and is
- // hardly needed _here_. Once we add automated reports for
- // the Taler auditor, we need to generate this data anyway
- // and should probably return it here as well.
- interface WireFeeConflictDetails {
- // Numerical `error code <error-codes>`:
- code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE";
-
- // Text describing the issue for humans.
- hint: string;
-
- // Wire fee (wrongly) charged by the exchange, breaking the
- // contract affirmed by the ``exchange_sig``.
- wire_fee: Amount;
-
- // Timestamp of the wire transfer.
- execution_time: Timestamp;
-
- // The expected wire fee (as signed by the exchange).
- expected_wire_fee: Amount;
-
- // Expected closing fee (needed to verify signature).
- expected_closing_fee: Amount;
-
- // Start date of the expected fee structure.
- start_date: Timestamp;
-
- // End date of the expected fee structure.
- end_date: Timestamp;
-
- // Signature of the exchange affirming the expected fee structure.
- master_sig: EddsaSignature;
-
- // Master public key of the exchange.
- master_pub: EddsaPublicKey;
- }
-
-
- .. ts:def:: TrackTransferConflictDetails
-
- interface TrackTransferConflictDetails {
- // Numerical `error code <error-codes>`.
- code: "TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS";
-
- // Text describing the issue for humans.
- hint: string;
-
- // Offset in the ``exchange_transfer`` where the
- // exchange's response fails to match the ``exchange_deposit_proof``.
- conflict_offset: number;
-
- // The response from the exchange which tells us when the
- // coin was returned to us, except that it does not match
- // the expected value of the coin.
- //
- // This field is NOT provided by the current implementation,
- // because this would be quite expensive to generate and is
- // hardly needed _here_. Once we add automated reports for
- // the Taler auditor, we need to generate this data anyway
- // and should probably return it here as well.
- exchange_transfer?: TrackTransferResponse;
-
- // Public key of the exchange used to sign the response to
- // our deposit request.
- deposit_exchange_pub: EddsaPublicKey;
-
- // Signature of the exchange signing the (conflicting) response.
- // Signs over a ``struct TALER_DepositConfirmationPS``.
- deposit_exchange_sig: EddsaSignature;
-
- // Hash of the merchant's bank account the wire transfer went to.
- h_wire: HashCode;
-
- // Hash of the contract terms with the conflicting deposit.
- h_contract_terms: HashCode;
-
- // At what time the exchange received the deposit. Needed
- // to verify the ``exchange_sig``.
- deposit_timestamp: Timestamp;
-
- // At what time the refund possibility expired (needed to verify ``exchange_sig``).
- refund_deadline: Timestamp;
-
- // Public key of the coin for which we have conflicting information.
- coin_pub: EddsaPublicKey;
-
- // Amount the exchange counted the coin for in the transfer.
- amount_with_fee: Amount;
-
- // Expected value of the coin.
- coin_value: Amount;
-
- // Expected deposit fee of the coin.
- coin_fee: Amount;
-
- // Expected deposit fee of the coin.
- deposit_fee: Amount;
-
- }
-
- .. ts:def:: TrackTransferProof
-
- interface TrackTransferProof {
- // Signature from the exchange made with purpose
- // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT``.
- exchange_sig: EddsaSignature;
-
- // Public EdDSA key of the exchange that was used to generate the signature.
- // Should match one of the exchange's signing keys from ``/keys``. Again given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- exchange_pub: EddsaSignature;
-
- // Hash of the wire details (identical for all deposits).
- // Needed to check the ``exchange_sig``
- h_wire: HashCode;
- }
-
Querying known wire transfers
-----------------------------
@@ -2576,419 +2759,218 @@ once we got a reply from the exchange.
The transfer cannot be deleted anymore.
---------------------
-Backend: Giving tips
---------------------
-
-Tips are a way for websites to give small amounts of e-cash to visitors (for
-example as a financial reward for providing information or watching
-advertizements). Tips are non-contractual: neither merchant nor consumer
-have any contractual information about the other party as a result of the
-tip.
-
-
-Create reserve
---------------
-
-Reserves are basically funds a merchant has provided
-to an exchange for a tipping campaign. Each reserve
-has a limited lifetime (say 2--4 weeks). Any funds
-not used to tip customers will automatically be wired
-back from the exchange to the originating account.
+-----------
+OTP Devices
+-----------
-To begin tipping, a merchant must tell the backend
-to set up a reserve. The backend will return a
-reserve public key which must be used as the wire
-transfer subject when wiring the tipping campaign
-funds to the exchange.
+OTP devices can be used to allow offline merchants
+to validate that a customer made a payment.
-.. _tips:
-.. http:post:: [/instances/$INSTANCE]/private/reserves
- Create a reserve for tipping.
+.. http:post:: [/instances/$INSTANCE]/private/otp-devices
- This request is **not** idempotent. However, while repeating
- it will create another reserve, that is generally pretty harmless
- (assuming only one of the reserves is filled with a wire transfer).
- Clients may want to eventually delete the unused reserves to
- avoid clutter.
+ This is used to associate an OTP device with an instance.
**Request:**
- The request body is a `ReserveCreateRequest` object.
+ The request must be a `OtpDeviceAddDetails`.
**Response:**
- :http:statuscode:`200 OK`:
- The backend is waiting for the reserve to be established. The merchant
- must now perform the wire transfer indicated in the `ReserveCreateConfirmation`.
- :http:statuscode:`408 Request timeout`:
- The exchange did not respond on time.
- :http:statuscode:`409 Conflict`:
- The exchange does not support the requested wire method.
- :http:statuscode:`502 Bad gateway`:
- We could not obtain ``/wire`` details from the specified exchange base URL.
- :http:statuscode:`504 Gateway timeout`:
- The merchant's interaction with the exchange took too long.
- The client might want to try again later.
-
- .. ts:def:: ReserveCreateRequest
-
- interface ReserveCreateRequest {
- // Amount that the merchant promises to put into the reserve.
- initial_balance: Amount;
-
- // Exchange the merchant intends to use for tipping.
- exchange_url: string;
-
- // Desired wire method, for example "iban" or "x-taler-bank".
- wire_method: string;
- }
-
- .. ts:def:: ReserveCreateConfirmation
-
- interface ReserveCreateConfirmation {
- // Public key identifying the reserve.
- reserve_pub: EddsaPublicKey;
-
- // Wire account of the exchange where to transfer the funds.
- payto_uri: string;
- }
-
-.. http:get:: [/instances/$INSTANCE]/private/reserves
-
- Obtain list of reserves that have been created for tipping.
-
- **Request:**
-
- :query after: *Optional*. Only return reserves created after the given timestamp in milliseconds.
- :query active: *Optional*. Only return active/inactive reserves depending on the boolean given.
- :query failures: *Optional*. Only return reserves where we disagree with the exchange about the initial balance.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- Returns a list of known tipping reserves.
- The body is a `TippingReserveStatus`.
-
- .. ts:def:: TippingReserveStatus
-
- interface TippingReserveStatus {
- // Array of all known reserves (possibly empty!).
- reserves: ReserveStatusEntry[];
- }
-
- .. ts:def:: ReserveStatusEntry
+ :http:statuscode:`204 No content`:
+ The creation of the template is successful.
+ :http:statuscode:`404 Not found`:
+ The merchant instance is unknown or it is not in our data.
- interface ReserveStatusEntry {
- // Public key of the reserve.
- reserve_pub: EddsaPublicKey;
+ .. ts:def:: OtpDeviceAddDetails
- // Timestamp when it was established.
- creation_time: Timestamp;
+ interface OtpDeviceAddDetails {
- // Timestamp when it expires.
- expiration_time: Timestamp;
+ // Device ID to use.
+ otp_device_id: string;
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
+ // Human-readable description for the device.
+ otp_device_description: string;
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
+ // A key encoded with RFC 3548 Base32.
+ // IMPORTANT: This is not using the typical
+ // Taler base32-crockford encoding.
+ // Instead it uses the RFC 3548 encoding to
+ // be compatible with the TOTP standard.
+ otp_key: string;
- // Amount picked up so far.
- pickup_amount: Amount;
+ // Algorithm for computing the POS confirmation.
+ // "NONE" or 0: No algorithm (no pos confirmation will be generated)
+ // "TOTP_WITHOUT_PRICE" or 1: Without amounts (typical OTP device)
+ // "TOTP_WITH_PRICE" or 2: With amounts (special-purpose OTP device)
+ // The "String" variants are supported @since protocol **v7**.
+ otp_algorithm: Integer | string;
- // Amount approved for tips that exceeds the pickup_amount.
- committed_amount: Amount;
-
- // Is this reserve active (false if it was deleted but not purged)?
- active: boolean;
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
}
-Query funds remaining
----------------------
+.. http:patch:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
-.. http:get:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB
-
- Obtain information about a specific reserve that have been created for tipping.
+ This is used to update an OTP device. It is useful when we need to change information in the OTP device or when we have mistake some information.
**Request:**
- :query tips: *Optional*. If set to "yes", returns also information about all of the tips created.
+ The request must be a `OtpDevicePatchDetails`.
**Response:**
- :http:statuscode:`200 OK`:
- Returns the `ReserveDetail`.
+ :http:statuscode:`204 No content`:
+ The template has successfully modified.
:http:statuscode:`404 Not found`:
- The tipping reserve is not known.
- :http:statuscode:`502 Bad gateway`:
- We are having trouble with the request because of a problem with the exchange.
- Likely returned with an "exchange_code" in addition to a "code" and
- an "exchange_http_status" in addition to our own HTTP status. Also usually
- includes the full exchange reply to our request under "exchange_reply".
- This is only returned if there was actual trouble with the exchange, not
- if the exchange merely did not respond yet or if it responded that the
- reserve was not yet filled.
- :http:statuscode:`504 Gateway timeout`:
- The merchant's interaction with the exchange took too long.
- The client might want to try again later.
-
- .. ts:def:: ReserveDetail
-
- interface ReserveDetail {
- // Timestamp when it was established.
- creation_time: Timestamp;
-
- // Timestamp when it expires.
- expiration_time: Timestamp;
-
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
-
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
-
- // Amount picked up so far.
- pickup_amount: Amount;
-
- // Amount approved for tips that exceeds the pickup_amount.
- committed_amount: Amount;
-
- // Array of all tips created by this reserves (possibly empty!).
- // Only present if asked for explicitly.
- tips?: TipStatusEntry[];
-
- // Is this reserve active (false if it was deleted but not purged)?
- active: boolean;
-
- // URI to use to fill the reserve, can be NULL
- // if the reserve is inactive or was already filled
- payto_uri: string;
-
- // URL of the exchange hosting the reserve,
- // NULL if the reserve is inactive
- exchange_url: string;
- }
-
- .. ts:def:: TipStatusEntry
+ The template(ID) is unknown to the backend.
+ :http:statuscode:`409 Conflict`:
+ The provided information is inconsistent with the current state of the template. Changes made is the same with
+ another store.
- interface TipStatusEntry {
+ .. ts:def:: OtpDevicePatchDetails
- // Unique identifier for the tip.
- tip_id: HashCode;
+ interface OtpDevicePatchDetails {
- // Total amount of the tip that can be withdrawn.
- total_amount: Amount;
-
- // Human-readable reason for why the tip was granted.
- reason: string;
- }
+ // Human-readable description for the device.
+ otp_device_description: string;
+ // A key encoded with RFC 3548 Base32.
+ // IMPORTANT: This is not using the typical
+ // Taler base32-crockford encoding.
+ // Instead it uses the RFC 3548 encoding to
+ // be compatible with the TOTP standard.
+ otp_key?: string;
-Authorizing tips
-----------------
+ // Algorithm for computing the POS confirmation.
+ otp_algorithm: Integer;
-.. http:post:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB/authorize-tip
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
- Authorize creation of a tip from the given reserve.
- **Request:**
+.. http:get:: [/instances/$INSTANCE]/private/otp-devices
- The request body is a `TipCreateRequest` object.
+ This is used to return the list of all the OTP devices.
**Response:**
:http:statuscode:`200 OK`:
- A tip has been created. The backend responds with a `TipCreateConfirmation`.
+ The backend has successfully returned all the templates. Returns a `OtpDeviceSummaryResponse`.
:http:statuscode:`404 Not found`:
- The instance or the reserve is unknown to the backend.
- :http:statuscode:`412 Precondition failed`:
- The tip amount requested exceeds the available reserve balance for tipping.
-
- .. ts:def:: TipCreateRequest
+ The backend has does not know about the instance.
- interface TipCreateRequest {
- // Amount that the customer should be tipped.
- amount: Amount;
+ .. ts:def:: OtpDeviceSummaryResponse
- // Justification for giving the tip.
- justification: string;
+ interface OtpDeviceSummaryResponse {
- // URL that the user should be directed to after tipping,
- // will be included in the tip_token.
- next_url: string;
+ // Array of devices that are present in our backend.
+ otp_devices: OtpDeviceEntry[];
}
- .. ts:def:: TipCreateConfirmation
-
- interface TipCreateConfirmation {
- // Unique tip identifier for the tip that was created.
- tip_id: HashCode;
+ .. ts:def:: OtpDeviceEntry
- // taler://tip URI for the tip.
- taler_tip_uri: string;
+ interface OtpDeviceEntry {
- // URL that will directly trigger processing
- // the tip when the browser is redirected to it.
- tip_status_url: string;
+ // Device identifier.
+ otp_device_id: string;
- // When does the tip expire?
- tip_expiration: Timestamp;
+ // Human-readable description for the device.
+ device_description: string;
}
+.. http:get:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
-.. http:post:: [/instances/$INSTANCE]/private/tips
+ This is used to obtain detailed information about a specific OTP device.
- Authorize creation of a tip from the given reserve, except with
- automatic selection of a working reserve of the instance by the
- backend. Intentionally otherwise identical to the ``/authorize-tip``
- endpoint given above.
+ The client can provide additional inputs in the query to allow the backend
+ to compute and return a sample OTP code. Note that it is not an error if
+ the client provides query arguments that are not being used *or* that are
+ insufficient for the server to compute the ``otp_code``: If the client
+ provides inadequate query parameters, the ``otp_code`` is simply omitted
+ from the response.
- **Request:**
+ **Query:**
- The request body is a `TipCreateRequest` object.
+ :query faketime=TIMESTAMP: *Optional*. Timestamp in seconds to use when calculating the current OTP code of the device. Since protocol **v10**.
+ :query price=AMOUNT: *Optional*. Price to use when calculating the current OTP code of the device. Since protocol **v10**.
**Response:**
:http:statuscode:`200 OK`:
- A tip has been created. The backend responds with a `TipCreateConfirmation`.
+ The backend has successfully returned the detailed information about a specific OTP device.
+ Returns a `OtpDeviceDetails`.
:http:statuscode:`404 Not found`:
- The instance is unknown to the backend.
- :http:statuscode:`412 Precondition failed`:
- The tip amount requested exceeds the available reserve balance for tipping
- in all of the reserves of the instance.
+ The OTP device or instance is unknown to the backend.
+ .. ts:def:: OtpDeviceDetails
-Deleting reserves
------------------
+ interface OtpDeviceDetails {
-.. http:delete:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB
+ // Human-readable description for the device.
+ device_description: string;
- Delete information about a reserve. Fails if the reserve still has
- committed to tips that were not yet picked up and that have not yet
- expired.
-
- **Request:**
-
- :query purge: *Optional*. If set to YES, the reserve and all information
- about tips it issued will be fully deleted.
- Otherwise only the private key would be deleted.
-
- **Response:**
-
- :http:statuscode:`204 No content`:
- The backend has successfully deleted the reserve.
- :http:statuscode:`404 Not found`:
- The backend does not know the instance or the reserve.
- :http:statuscode:`409 Conflict`:
- The backend refuses to delete the reserve (committed tips awaiting pickup).
-
-
-Checking tip status
--------------------
-
-.. http:get:: [/instances/$INSTANCE]/private/tips/$TIP_ID
+ // Algorithm for computing the POS confirmation.
+ //
+ // Currently, the following numbers are defined:
+ // 0: None
+ // 1: TOTP without price
+ // 2: TOTP with price
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+
+ // Current time for time-based OTP devices.
+ // Will match the ``faketime`` argument of the
+ // query if one was present, otherwise the current
+ // time at the backend.
+ //
+ // Available since protocol **v10**.
+ otp_timestamp: Integer;
- Obtain information about a particular tip.
+ // Current OTP confirmation string of the device.
+ // Matches exactly the string that would be returned
+ // as part of a payment confirmation for the given
+ // amount and time (so may contain multiple OTP codes).
+ //
+ // If the ``otp_algorithm`` is time-based, the code is
+ // returned for the current time, or for the ``faketime``
+ // if a TIMESTAMP query argument was provided by the client.
+ //
+ // When using OTP with counters, the counter is **NOT**
+ // increased merely because this endpoint created
+ // an OTP code (this is a GET request, after all!).
+ //
+ // If the ``otp_algorithm`` requires an amount, the
+ // ``amount`` argument must be specified in the
+ // query, otherwise the ``otp_code`` is not
+ // generated.
+ //
+ // This field is *optional* in the response, as it is
+ // only provided if we could compute it based on the
+ // ``otp_algorithm`` and matching client query arguments.
+ //
+ // Available since protocol **v10**.
+ otp_code?: string;
- **Request:**
+ }
- :query pickups: If set to "yes", returns also information about all of the pickups.
+.. http:delete:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
**Response:**
- :http:statuscode:`200 OK`:
- The tip is known. The backend responds with a `TipDetails` message.
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the OTP device.
:http:statuscode:`404 Not found`:
- The tip is unknown to the backend.
-
- .. ts:def:: TipDetails
+ The backend does not know the instance or the OTP device.
- interface TipDetails {
- // Amount that we authorized for this tip.
- total_authorized: Amount;
- // Amount that was picked up by the user already.
- total_picked_up: Amount;
-
- // Human-readable reason given when authorizing the tip.
- reason: string;
-
- // Timestamp indicating when the tip is set to expire (may be in the past).
- expiration: Timestamp;
-
- // Reserve public key from which the tip is funded.
- reserve_pub: EddsaPublicKey;
-
- // Array showing the pickup operations of the wallet (possibly empty!).
- // Only present if asked for explicitly.
- pickups?: PickupDetail[];
- }
-
- .. ts:def:: PickupDetail
-
- interface PickupDetail {
- // Unique identifier for the pickup operation.
- pickup_id: HashCode;
-
- // Number of planchets involved.
- num_planchets: Integer;
-
- // Total amount requested for this pickup_id.
- requested_amount: Amount;
- }
-
-
-.. http:get:: [/instances/$INSTANCE]/private/tips
-
- Return the list of all tips.
-
- **Request:**
-
- :query include_expired: *Optional*. If set to "yes", the result includes expired tips also. Otherwise, only active tips are returned.
-
- :query limit: *Optional*. At most return the given number of results. Negative for descending in database row id, positive for ascending in database row id.
-
- :query offset: *Optional*. Starting ``row_id`` for an iteration.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The backend has successfully found the list of tips. The backend responds
- with a `TipsResponse`.
-
- .. ts:def:: TipsResponse
-
- interface TipsResponse {
-
- // List of tips that are present in the backend.
- tips: Tip[];
- }
-
- .. ts:def:: Tip
-
- interface Tip {
-
- // ID of the tip in the backend database.
- row_id: number;
-
- // Unique identifier for the tip.
- tip_id: HashCode;
-
- // (Remaining) amount of the tip (including fees).
- tip_amount: Amount;
- }
-
-
-
---------
-Template
---------
+---------
+Templates
+---------
The template is a backend feature that is used to allow wallets to create an
order. This is useful in cases where a store does not have Internet
@@ -3065,16 +3047,31 @@ Adding templates
// Human-readable description for the template.
template_description: string;
- // A base64-encoded key of the point-of-sale.
+ // OTP device ID.
// This parameter is optional.
- pos_key?: string;
+ otp_id?: string;
- // Algorithm for computing the POS confirmation, 0 for none.
- pos_algorithm?: Integer;
-
- // Additional information in a separate template.
+ // Fixed contract information for orders created from
+ // this template.
template_contract: TemplateContractDetails;
- }
+
+ // Key-value pairs matching a subset of the
+ // fields from `template_contract` that are
+ // user-editable defaults for this template.
+ // Since protocol **v13**.
+ editable_defaults?: Object;
+
+ // Required currency for payments. Useful if no
+ // amount is specified in the `template_contract`
+ // but the user should be required to pay in a
+ // particular currency anyway. Merchant backends
+ // may reject requests if the `template_contract`
+ // or `editable_defaults` do
+ // specify an amount in a different currency.
+ // This parameter is optional.
+ // Since protocol **v13**.
+ required_currency?: string;
+ }
.. ts:def:: TemplateContractDetails
@@ -3084,6 +3081,13 @@ Adding templates
// Human-readable summary for the template.
summary?: string;
+ // Required currency for payments to the template.
+ // The user may specify any amount, but it must be
+ // in this currency.
+ // This parameter is optional and should not be present
+ // if "amount" is given.
+ currency?: string;
+
// The price is imposed by the merchant and cannot be changed by the customer.
// This parameter is optional.
amount?: Amount;
@@ -3129,16 +3133,29 @@ Editing templates
// Human-readable description for the template.
template_description: string;
- // A base64-encoded key of the point-of-sale.
+ // OTP device ID.
// This parameter is optional.
- pos_key?: string;
-
- // Algorithm for computing the POS confirmation, 0 for none.
- pos_algorithm?: Integer;
+ otp_id?: string;
// Additional information in a separate template.
template_contract: TemplateContractDetails;
+ // Key-value pairs matching a subset of the
+ // fields from `template_contract` that are
+ // user-editable defaults for this template.
+ // Since protocol **v13**.
+ editable_defaults?: Object;
+
+ // Required currency for payments. Useful if no
+ // amount is specified in the `template_contract`
+ // but the user should be required to pay in a
+ // particular currency anyway. Merchant backends
+ // may reject requests if the `template_contract`
+ // or `editable_defaults` do
+ // specify an amount in a different currency.
+ // This parameter is optional.
+ // Since protocol **v13**.
+ required_currency?: string;
}
@@ -3163,7 +3180,7 @@ Inspecting template
interface TemplateSummaryResponse {
// List of templates that are present in our backend.
- templates_list: TemplateEntry[];
+ templates: TemplateEntry[];
}
The `TemplateEntry` object describes a template. It has the following structure:
@@ -3191,7 +3208,7 @@ Inspecting template
The backend has successfully returned the detailed information about a specific template.
Returns a `TemplateDetails`.
:http:statuscode:`404 Not found`:
- The template(ID) is unknown to the backend.
+ The instance or template(ID) is unknown to the backend.
.. ts:def:: TemplateDetails
@@ -3201,16 +3218,30 @@ Inspecting template
// Human-readable description for the template.
template_description: string;
- // A base64-encoded key of the point-of-sale.
+ // OTP device ID.
// This parameter is optional.
- pos_key?: string;
-
- // Algorithm for computing the POS confirmation, 0 for none.
- pos_algorithm?: Integer;
+ otp_id?: string;
// Additional information in a separate template.
template_contract: TemplateContractDetails;
- }
+
+ // Key-value pairs matching a subset of the
+ // fields from `template_contract` that are
+ // user-editable defaults for this template.
+ // Since protocol **v13**.
+ editable_defaults?: Object;
+
+ // Required currency for payments. Useful if no
+ // amount is specified in the `template_contract`
+ // but the user should be required to pay in a
+ // particular currency anyway. Merchant backends
+ // may reject requests if the `template_contract`
+ // or `editable_defaults` do
+ // specify an amount in a different currency.
+ // This parameter is optional.
+ // Since protocol **v13**.
+ required_currency?: string;
+ }
@@ -3231,7 +3262,48 @@ Removing template
Using template
-----------------
+--------------
+
+.. http:get:: [/instances/$INSTANCE]/templates/$TEMPLATE_ID
+
+ This is used to obtain information about a specific template by wallets
+ before they ask the user to fill in details.
+ This endpoint is available since protocol **v11**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the detailed information about a specific template.
+ Returns a `WalletTemplateDetails`.
+ :http:statuscode:`404 Not found`:
+ The instance or template(ID) is unknown to the backend.
+
+ .. ts:def:: WalletTemplateDetails
+
+ interface WalletTemplateDetails {
+
+ // Hard-coded information about the contrac terms
+ // for this template.
+ template_contract: TemplateContractDetails;
+
+ // Key-value pairs matching a subset of the
+ // fields from `template_contract` that are
+ // user-editable defaults for this template.
+ // Since protocol **v13**.
+ editable_defaults?: Object;
+
+ // Required currency for payments. Useful if no
+ // amount is specified in the `template_contract`
+ // but the user should be required to pay in a
+ // particular currency anyway. Merchant backends
+ // may reject requests if the `template_contract`
+ // or `editable_defaults` do
+ // specify an amount in a different currency.
+ // This parameter is optional.
+ // Since protocol **v13**.
+ required_currency?: string;
+ }
+
.. http:post:: [/instances/$INSTANCES]/templates/$TEMPLATE_ID
@@ -3288,7 +3360,7 @@ Adding webhooks
The creation of the webhook is successsful.
:http:statuscode:`404 Not found`:
- The merchant instance is unknowm or it not in our data.
+ The merchant instance is unknown or it not in our data.
.. ts:def:: WebhookAddDetails
@@ -3449,6 +3521,242 @@ Removing webhook
+----------------------------------------
+Token Families: Subscriptions, Discounts
+----------------------------------------
+
+This API provides functionalities for the issuance, management, and revocation
+of token families. Tokens facilitate the implementation of subscriptions and
+discounts, engaging solely the merchant and the user. Each token family
+encapsulates details pertaining to its respective tokens, guiding the merchant's
+backend on the appropriate processing and handling.
+
+
+Creating token families
+-----------------------
+
+.. http:post:: [/instances/$INSTANCES]/private/tokenfamilies
+
+ This is used to create a token family.
+
+ **Request:**
+
+ The request must be a `TokenFamilyCreateRequest`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The token family was created successfully.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the instance.
+
+ .. ts:def:: TokenFamilyCreateRequest
+
+ interface TokenFamilyCreateRequest {
+
+ // Identifier for the token family consisting of unreserved characters
+ // according to RFC 3986.
+ slug: string;
+
+ // 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.
+ description_i18n?: { [lang_tag: string]: string };
+
+ // Start time of the token family's validity period.
+ // If not specified, merchant backend will use the current time.
+ valid_after?: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Validity duration of an issued token.
+ duration: RelativeTime;
+
+ // Kind of the token family.
+ kind: TokenFamilyKind;
+
+ }
+
+ .. ts:def:: TokenFamilyKind
+
+ enum TokenFamilyKind {
+ Discount = "discount",
+ Subscription = "subscription",
+ }
+
+
+Updating token families
+-----------------------
+
+.. http:patch:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
+
+ This is used to update a token family.
+
+ **Request:**
+
+ The request must be a `TokenFamilyUpdateRequest`.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The token family was successsful updated. Returns a `TokenFamilyDetails`.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the token family or instance.
+
+ .. ts:def:: TokenFamilyUpdateRequest
+
+ interface TokenFamilyUpdateRequest {
+
+ // 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.
+ description_i18n: { [lang_tag: string]: string };
+
+ // Start time of the token family's validity period.
+ valid_after: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Validity duration of an issued token.
+ duration: RelativeTime;
+
+ }
+
+
+
+Inspecting token families
+-------------------------
+
+.. http:get:: [/instances/$INSTANCES]/private/tokenfamilies
+
+ This is used to list all configured token families for an instance.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The merchant backend has successfully returned all token families.
+ Returns a `TokenFamiliesList`.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the instance.
+
+ .. ts:def:: TokenFamiliesList
+
+ // TODO: Add pagination
+
+ interface TokenFamiliesList {
+
+ // All configured token families of this instance.
+ token_families: TokenFamilySummary[];
+
+ }
+
+ .. ts:def:: TokenFamilySummary
+
+ interface TokenFamilySummary {
+ // Identifier for the token family consisting of unreserved characters
+ // according to RFC 3986.
+ slug: string;
+
+ // Human-readable name for the token family.
+ name: string;
+
+ // Start time of the token family's validity period.
+ valid_after: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Kind of the token family.
+ kind: TokenFamilyKind;
+ }
+
+
+.. http:get:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
+
+ This is used to get detailed information about a specific token family.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The merchant backend has successfully returned the detailed information
+ about a specific token family. Returns a `TokenFamilyDetails`.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the token family or instance.
+
+
+ The `TokenFamilyDetails` object describes a configured token family.
+
+ .. ts:def:: TokenFamilyDetails
+
+ interface TokenFamilyDetails {
+
+ // Identifier for the token family consisting of unreserved characters
+ // according to RFC 3986.
+ slug: string;
+
+ // 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.
+ description_i18n?: { [lang_tag: string]: string };
+
+ // Start time of the token family's validity period.
+ valid_after: Timestamp;
+
+ // End time of the token family's validity period.
+ valid_before: Timestamp;
+
+ // Validity duration of an issued token.
+ duration: RelativeTime;
+
+ // Kind of the token family.
+ kind: TokenFamilyKind;
+
+ // How many tokens have been issued for this family.
+ issued: Integer;
+
+ // How many tokens have been redeemed for this family.
+ redeemed: Integer;
+
+ }
+
+
+
+Deleting token families
+-----------------------
+
+.. http:delete:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
+
+ This is used to delete a token family. Issued tokens of this family will not
+ be spendable anymore.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the token family.
+
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the token family or instance.
+
+
+
------------------
The Contract Terms
------------------
@@ -3478,6 +3786,10 @@ The contract terms must have the following structure:
// encoded in it (such as a short product identifier and timestamp).
order_id: string;
+ // List of contract choices that the customer can select from.
+ // @since protocol **vSUBSCRIBE**
+ choices: ContractChoice[];
+
// Total price for the transaction.
// The exchange will subtract deposit fees from that amount
// before transferring it to the merchant.
@@ -3490,13 +3802,26 @@ The contract terms must have the following structure:
public_reorder_url?: string;
// URL that will show that the order was successful after
- // it has been paid for. Optional. When POSTing to the
- // merchant, the placeholder "${ORDER_ID}" will be
- // replaced with the actual order ID (useful if the
+ // it has been paid for. Optional, but either ``fulfillment_url``
+ // or ``fulfillment_message`` must be specified in every
+ // contract terms.
+ //
+ // If a non-unique fulfillment URL is used, a customer can only
+ // buy the order once and will be redirected to a previous purchase
+ // when trying to buy an order with the same fulfillment URL a second
+ // time. This is useful for digital goods that a customer only needs
+ // to buy once but should be able to repeatedly download.
+ //
+ // For orders where the customer is expected to be able to make
+ // repeated purchases (for equivalent goods), the fulfillment URL
+ // should be made unique for every order. The easiest way to do
+ // this is to include a unique order ID in the fulfillment URL.
+ //
+ // When POSTing to the merchant, the placeholder text "${ORDER_ID}"
+ // is be replaced with the actual order ID (useful if the
// order ID is generated server-side and needs to be
- // in the URL).
- // Note that this placeholder can only be used once.
- // Either fulfillment_url or fulfillment_message must be specified.
+ // in the URL). Note that this placeholder can only be used once.
+ // Front-ends may use other means to generate a unique fulfillment URL.
fulfillment_url?: string;
// Message shown to the customer after paying for the order.
@@ -3508,23 +3833,9 @@ The contract terms must have the following structure:
fulfillment_message_i18n?: { [lang_tag: string]: string };
// Maximum total deposit fee accepted by the merchant for this contract.
+ // Overrides defaults of the merchant instance.
max_fee: Amount;
- // Maximum wire fee accepted by the merchant (customer share to be
- // divided by the ``wire_fee_amortization`` factor, and further reduced
- // if deposit fees are below ``max_fee``). Default if missing is zero.
- max_wire_fee: Amount;
-
- // Over how many customer transactions does the merchant expect to
- // amortize wire fees on average? If the exchange's wire fee is
- // above ``max_wire_fee``, the difference is divided by this number
- // to compute the expected customer's contribution to the wire fee.
- // The customer's contribution may further be reduced by the difference
- // between the ``max_fee`` and the sum of the actual deposit fees.
- // Optional, default value if missing is 1. 0 and negative values are
- // invalid and also interpreted as 1.
- wire_fee_amortization: number;
-
// List of products that are part of the purchase (see `Product`).
products: Product[];
@@ -3561,9 +3872,6 @@ The contract terms must have the following structure:
// The wire transfer fees must be added based on this wire transfer method.
wire_method: string;
- // Any exchanges audited by these auditors are accepted by the merchant.
- auditors: Auditor[];
-
// Exchanges that the merchant accepts even if it does not accept any auditors that audit them.
exchanges: Exchange[];
@@ -3600,13 +3908,105 @@ The contract terms must have the following structure:
// Extra data that is only interpreted by the merchant frontend.
// Useful when the merchant needs to store extra information on a
// contract without storing it separately in their database.
- extra?: any;
+ // Must really be an Object (not a string, integer, float or array).
+ extra?: Object;
+
+ // Minimum age the buyer must have (in years). Default is 0.
+ // This value is at least as large as the maximum over all
+ // mimimum age requirements of the products in this contract.
+ // It might also be set independent of any product, due to
+ // legal requirements.
+ minimum_age?: Integer;
+
}
The wallet must select an exchange that either the merchant accepts directly by
listing it in the exchanges array, or for which the merchant accepts an auditor
that audits that exchange by listing it in the auditors array.
+The `ContractChoice` object describes a possible choice within a contract. The
+choice is done by the customer and consists of in- and outputs. In the example
+of buying an article, the merchant could present the customer with the choice
+to use a valid subscription token or pay using a gift voucher. Available since
+protocol **vSUBSCRIBE**.
+
+.. ts:def:: ContractChoice
+
+ interface ContractChoice {
+ // Inputs that must be provided by the customer, if this choice is selected.
+ inputs: ContractInput[];
+
+ // Outputs provided by the merchant, if this choice is selected.
+ outputs: ContractOutput[];
+ }
+
+.. ts:def:: ContractInput
+
+ // For now, only token inputs are supported.
+ type ContractInput = ContractInputToken;
+
+.. ts:def:: ContractInputToken
+
+ interface ContractInputToken {
+
+ // Token input.
+ type: "token";
+
+ // How many units of the input are required. Defaults to 1 if not specified.
+ // Input with count == 0 are ignored by the merchant backend.
+ count?: Integer;
+
+ // Token family slug as configured in the merchant backend. Slug is unique
+ // across all configured tokens of a merchant.
+ token_family_slug: string;
+
+ // TODO: Is this only relevant for subscriptions or also for discounts?
+ // Start of the validity period of the subscription token. Based on this,
+ // the merchant will select the relevant signing key. Is null for discounts.
+ valid_after?: Timestamp;
+
+ }
+
+.. ts:def:: ContractOutput
+
+ type ContractOutput = ContractOutputToken | ContractOutputTaxReceipt;
+
+.. ts:def:: ContractOutputToken
+
+ interface ContractOutputToken {
+
+ // Token output.
+ type: "token";
+
+ // How many units of the output are yielded. Defaults to 1 if not specified.
+ // Output with number == 0 are ignored by the merchant backend.
+ number?: Integer;
+
+ // Token family slug as configured in the merchant backend. Slug is unique
+ // across all configured tokens of a merchant.
+ // TODO: Should we rename this to token_family_slug?
+ authority_label: string;
+
+ // TODO: Is this only relevant for subscriptions or also for discounts?
+ // Start of the validity period of the subscription token. Based on this,
+ // the merchant will select the relevant signing key. Is null for discounts.
+ valid_after?: Timestamp;
+
+ }
+
+.. ts:def:: ContractOutputTaxReceipt
+
+ interface ContractOutputTaxReceipt {
+
+ // Tax receipt output.
+ type: "tax-receipt";
+
+ // Base URL of the donation authority that will
+ // issue the tax receipt.
+ donau_url: string;
+
+ }
+
The `Product` object describes the product being purchased from the merchant.
It has the following structure:
@@ -3731,6 +4131,18 @@ It has the following structure:
// The exchange's base URL.
url: string;
+ // How much would the merchant like to use this exchange.
+ // The wallet should use a suitable exchange with high
+ // priority. The following priority values are used, but
+ // it should be noted that they are NOT in any way normative.
+ //
+ // 0: likely it will not work (recently seen with account
+ // restriction that would be bad for this merchant)
+ // 512: merchant does not know, might be down (merchant
+ // did not yet get /wire response).
+ // 1024: good choice (recently confirmed working)
+ priority: Integer;
+
// Master public key of the exchange.
master_pub: EddsaPublicKey;
}
diff --git a/core/api-overview.rst b/core/api-overview.rst
new file mode 100644
index 00000000..576b4c4a
--- /dev/null
+++ b/core/api-overview.rst
@@ -0,0 +1,150 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Florian Dold
+ @author Benedikt Muller
+ @author Sree Harsha Totakura
+ @author Marcello Stanisci
+ @author Christian Grothoff
+
+========
+Overview
+========
+
+.. rubric:: Taler Exchange Public API
+
+* **Summary**: Public API for the payment service provider component of GNU Taler.
+* **Providers**: GNU Taler Exchange
+* **Consumers**: Wallet, Merchant
+* :doc:`Docs <api-exchange>`
+
+.. rubric:: Taler Exchange Management API
+
+* **Summary**: Management API for the payment service provider component of GNU Taler.
+* **Providers**: GNU Taler Exchange
+* **Consumers**: Exchange tooling (``taler-exchange-offline``), Auditor
+* :doc:`Docs <api-exchange>`
+
+.. rubric:: Taler Merchant Backend Public API
+
+* **Summary**: Allows communication between merchants and users' wallets.
+* **Providers**: GNU Taler Merchant backend
+* **Consumers**: Wallet
+* :doc:`Docs <api-merchant>`
+
+.. rubric:: Taler Merchant Backend Private API
+
+* **Summary**: Allows the merchant to manage Taler-based payments and related functionality.
+* **Providers**: GNU Taler Merchant backend
+* **Consumers**: Merchant's shop Website backend, Merchant PoS app, Merchant Backoffice UI
+* :doc:`Docs <api-merchant>`
+
+.. rubric:: Taler Wallet Core API
+
+* **Summary**: API to access functionality of the Taler Wallet service running locally on user's devices.
+* **Providers**: wallet-core
+* **Consumers**: UIs for the GNU Taler wallet
+
+.. rubric:: Core Bank API
+
+* **Summary**: Protocol to manage a simple core bank with optional regional
+ currency support. Allows access to a bank account by the owner of the
+ account. The owner can access the account balance, transaction list, and initate
+ payments.
+* **Providers**: LibEuFin demobank, Taler Fakebank (partial)
+* **Consumers**: Cashier App, demobank-ui
+* :doc:`Docs <api-corebank>`
+
+.. rubric:: Taler Bank Integration API
+
+* **Summary**: Offered by banks to provide the wallet/user with more information about ongoing withdrawals of Taler digital cash.
+* **Providers**: Taler fakebank, LibEuFin demobank, Banks (that provide extra Taler support)
+* **Consumers**: Taler Wallet
+* :doc:`Docs <api-bank-integration>`
+
+.. rubric:: Taler Wire Gateway API
+
+* **Summary**: Allows the Taler Exchange to query incoming transactions and initiate payments with a protocol that abstracts away details of the underlying banking system.
+
+* **Providers**: Taler fakebank, LibEuFin Nexus, Depoloymerization wire gateway
+
+* **Consumers**: GNU Taler Exchange, Wire Auditor
+
+* :doc:`Docs <api-bank-wire>`
+
+.. rubric:: Taler Bank Revenue API
+
+* **Summary**: Offered by banks to provide clients the ability to download credit transaction histories.
+* **Providers**: Taler fakebank, LibEuFin demobank, Banks (that provide extra Taler support)
+* **Consumers**: Taler Merchant, GNU Anastasis
+* :doc:`Docs <api-bank-revenue>`
+
+
+.. rubric:: Taler Sync API
+
+* **Summary**: Encrypted Data blob storage and retrieval API with payments for storage handled by GNU Taler payments.
+
+* **Providers**: GNU Taler Sync service
+
+* **Consumers**: Taler Wallet
+
+* :doc:`Docs <api-sync>`
+
+.. rubric:: Taler Auditor API
+
+* **Summary**: Reporting of certain transactions or potential problems directly to the auditor.
+* **Providers**: GNU Taler Auditor service
+* **Consumers**: GNU Taler Merchant, eventually Taler Wallet
+* :doc:`Docs <api-auditor>`
+
+.. rubric:: Taldir API
+
+* **Summary**: Looking up of Taler mailboxes associated with particular Internet service addresses.
+* **Providers**: GNU TalDir service
+* **Consumers**: GNU Taler Wallet
+* :doc:`Docs <api-taldir>`
+
+.. rubric:: Taler Mailbox API
+
+* **Summary**: Tansmission of encrypted payment messages between Taler wallets.
+* **Providers**: GNU Taler Mailbox service
+* **Consumers**: GNU Taler Wallet
+* :doc:`Docs <api-mailbox>`
+
+.. rubric:: Anastasis Provider Public API
+
+* **Summary**: Backup for secret splitting backup and recovery with GNU Anastasis providers.
+* **Providers**: GNU Anastasis providers
+* **Consumers**: Anastasis core client implementations (C implementation, TypeScript implementation)
+
+.. rubric:: Anastasis Reducer API
+
+* **Summary**: API used to step through the backup and recovery process of GNU Anastasis.
+* **Providers**: Anastasis core client implementations (C implementation, TypeScript implementation)
+* **Consumers**: Anastasis UIs (CLI, GTK, anastasis-webui)
+
+.. rubric:: LibEuFin Nexus API
+
+* **Summary**: API used to configure and access LibEuFin nexus, a generic server that supports multiple protocols to access a bank account.
+
+* **Providers**: LibEuFin Nexus service
+
+* **Consumers**: ``libeufin-cli``, (future) LibEuFin Web UI
+
+.. rubric:: EBICS
+
+* **Summary**: Allows businesses/banks/consumers to exchange data with a bank's core banking system.
+* **Consumers**: LibEuFin Nexus
+* **Providers**: libeufin-bank, Banks
diff --git a/core/api-sync.rst b/core/api-sync.rst
index 51e429e4..c2c86c23 100644
--- a/core/api-sync.rst
+++ b/core/api-sync.rst
@@ -18,7 +18,7 @@
.. _sync-api:
======================================
-Backup and Synchronization Service API
+Backup and Synchronization RESTful API
======================================
The backup and synchronization service uses an EdDSA key
@@ -102,6 +102,9 @@ over TLS, and that the synchronization service is trusted to not build
user's location profiles by linking client IP addresses and client
keys.
+.. contents:: Table of Contents
+ :local:
+
.. include:: tos.rst
@@ -112,6 +115,7 @@ Receiving Configuration
.. http:get:: /config
Obtain the key configuration settings of the storage service.
+ This specification corresponds to ``current`` protocol being version **2**.
**Response:**
@@ -137,6 +141,10 @@ Receiving Configuration
// The format is "current:revision:age".
version: string;
+ // URN of the implementation (needed to interpret 'revision' in version).
+ // @since v2, may become mandatory in the future.
+ implementation?: string;
+
}
.. _sync:
diff --git a/core/api-taldir.rst b/core/api-taldir.rst
index 1639e36d..4da9bb02 100644
--- a/core/api-taldir.rst
+++ b/core/api-taldir.rst
@@ -34,6 +34,8 @@ for all details not specified in the individual requests.
The `glossary <https://docs.taler.net/glossary.html#glossary>`_
defines all specific terms used in this section.
+.. contents:: Table of Contents
+ :local:
.. include:: tos.rst
diff --git a/core/api-error.rst b/core/index-bank-apis.rst
index f3bf494c..f108df32 100644
--- a/core/api-error.rst
+++ b/core/index-bank-apis.rst
@@ -1,6 +1,5 @@
..
This file is part of GNU TALER.
-
Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
@@ -14,28 +13,26 @@
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ @author Florian Dold
+ @author Benedikt Muller
+ @author Sree Harsha Totakura
@author Marcello Stanisci
@author Christian Grothoff
-.. _error-codes:
-.. _TALER_ErrorCode:
-
------------
-Error Codes
------------
-
-All error codes used in GNU Taler are defined in
-`GANA <https://git.gnunet.org/gana.git/tree/gnu-taler-error-codes/>`__.
+=================
+Bank RESTful APIs
+=================
-This centralized registry also contains generators that create enumerations
-and mappings from error codes to HTTP status codes and human-readable error
-messages for various programming languages.
-All error codes have numeric values below 100 or above 1000, so as to never be
-confused with HTTP status codes. A value of 0 is reserved for "no error" or
-"success".
+.. toctree::
+ :maxdepth: 1
-In C, the respective enumeration is the ``enum TALER_ErrorCode``.
+ intro-bank-apis
+ api-corebank
+ api-bank-wire
+ api-bank-revenue
+ api-bank-integration
+ api-bank-conversion-info
-Developers may have to re-run ``bootstrap`` and/or update their Git
-submodules to ensure that they have the lastest GANA registry.
+.. toctree::
+ :hidden:
diff --git a/core/index.rst b/core/index.rst
index 2a8352db..8a764c10 100644
--- a/core/index.rst
+++ b/core/index.rst
@@ -26,200 +26,26 @@ Core Protocol Specification
This chapter describes the APIs used in the GNU Taler project. It includes
both APIs that are pre-existing as well as APIs specific to the project.
-These *protocol specification* defines the HTTP-based, predominantly RESTful
-interfaces between the core components of Taler.
+These *protocol specifications* define the interfaces between the core
+components of GNU Taler. Most of these interfaces use HTTP-based RESTful
+protocols using JSON.
.. toctree::
- :maxdepth: 2
+ :maxdepth: 1
+ api-overview
api-common
- api-error
api-exchange
api-merchant
+ ../wallet/wallet-core
api-auditor
api-sync
+ api-challenger
api-taldir
api-mailbox
- api-wire
- api-bank-merchant
- api-bank-integration
- api-bank-access
+ index-bank-apis
+ api-donau
.. toctree::
:hidden:
-
-========
-Overview
-========
-
- * Taler Exchange Public API
-
-
- **Summary**: Public API for the payment service provider component of GNU Taler.
-
- **Providers**: GNU Taler Exchange
-
- **Consumers**: Wallet, Merchant
-
- :doc:`Docs <api-exchange>`
-
-
- * Taler Exchange Management API
-
- **Summary**: Management API for the payment service provider component of GNU Taler.
-
- **Providers**: GNU Taler Exchange
-
- **Consumers**: Exchange tooling (``taler-exchange-offline``), Auditor
-
- :doc:`Docs <api-exchange>`
-
-
- * Taler Merchant Backend Public API
-
- **Summary**: Allows communication between merchants and users' wallets.
-
- **Providers**: GNU Taler Merchant backend
-
- **Consumers**: Wallet
-
- :doc:`Docs <api-merchant>`
-
- * Taler Merchant Backend Private API
-
- **Summary**: Allows the merchant to manage Taler-based payments and related functionality.
-
- **Providers**: GNU Taler Merchant backend
-
- **Consumers**: Merchant's shop Website backend, Merchant PoS app, Merchant Backoffice UI
-
- :doc:`Docs <api-merchant>`
-
- * Taler Wallet Core API
-
- **Summary**: API to access functionality of the Taler Wallet service running locally on user's devices.
-
- **Providers**: wallet-core
-
- **Consumers**: UIs for the GNU Taler wallet
-
- * Taler Bank Integration API
-
- **Summary**: Offered by banks to provide the wallet/user with more information about ongoing withdrawals of Taler digital cash.
-
- **Providers**: Taler fakebank, LibEuFin demobank, Banks (that provide extra Taler support)
-
- **Consumers**: Taler Wallet
-
- :doc:`Docs <api-bank-integration>`
-
- * Taler Bank Access API
-
- **Summary**: Protocol to manage access to a bank account by the owner of the account. Allows access to account balance, transaction list, payment initiation.
-
- **Providers**: LibEuFin demobank, Taler Fakebank (partial)
-
- **Consumers**: Cashier App, demobank-ui
-
- :doc:`Docs <api-bank-access>`
-
-
- * Taler Bank Circuits API
-
- **Summary**: Allows management of bank user accounts in a regional currency bank deployment.
-
- **Providers**: LibEuFin Sandbox demobank
-
- **Consumers**: demobank-ui
-
- * Taler Wire Gateway API
-
- **Summary**: Allows the Taler Exchange to query incoming transactions and initiate payments with a protocol that abstracts away details of the underlying banking system.
-
- **Providers**: Taler fakebank, LibEuFin Nexus, Depoloymerization wire gateway
-
- **Consumers**: GNU Taler Exchange, Wire Auditor
-
- :doc:`Docs <api-wire>`
-
- * Taler Sync API
-
- **Summary**: Encrypted Data blob storage and retrieval API with payments for storage handled by GNU Taler payments.
-
- **Providers**: GNU Taler Sync service
-
- **Consumers**: Taler Wallet
-
- :doc:`Docs <api-sync>`
-
- * Taler Auditor API
-
- **Summary**: Reporting of certain transactions or potential problems directly to the auditor.
-
- **Providers**: GNU Taler Auditor service
-
- **Consumers**: GNU Taler Merchant, eventually Taler Wallet
-
- :doc:`Docs <api-auditor>`
-
- * Taldir API
-
- **Summary**: Looking up of Taler mailboxes associated with particular Internet service addresses.
-
- **Providers**: GNU TalDir service
-
- **Consumers**: GNU Taler Wallet
-
- :doc:`Docs <api-taldir>`
-
- * Taler Mailbox API
-
- **Summary**: Tansmission of encrypted payment messages between Taler wallets.
-
- **Providers**: GNU Taler Mailbox service
-
- **Consumers**: GNU Taler Wallet
-
- :doc:`Docs <api-mailbox>`
-
- * Anastasis Provider Public API
-
- **Summary**: Backup for secret splitting backup and recovery with GNU Anastasis providers.
-
- **Providers**: GNU Anastasis providers
-
- **Consumers**: Anastasis core client implementations (C implementation, TypeScript implementation)
-
- * Anastasis Reducer API
-
- **Summary**: API used to step through the backup and recovery process of GNU Anastasis.
-
- **Providers**: Anastasis core client implementations (C implementation, TypeScript implementation)
-
- **Consumers**: Anastasis UIs (CLI, GTK, anastasis-webui)
-
- * LibEuFin Nexus API
-
- **Summary**: API used to configure and access LibEuFin nexus, a generic server that supports multiple protocols to access a bank account.
-
- **Providers**: LibEuFin Nexus service
-
- **Consumers**: ``libeufin-cli``, (future) LibEuFin Web UI
-
- * LibEuFin Sandbox API
-
- **Summary** API used to configure the LibEuFin sandbox, a simulator for various protocols related to core banking that would typically offered by banks.
-
- **Providers**: LibEuFin Sandbox service
-
- **Consumers**: ``libeufin-cli``, (future) LibEuFin Web UI
-
- * EBICS
-
- **Summary**: Allows businesses/banks/consumers to exchange data with a bank's core banking system.
-
- **Consumers**: LibEuFin Nexus
-
- **Providers**: LibEuFin Sandbox, Banks
-
diff --git a/core/intro-bank-apis.rst b/core/intro-bank-apis.rst
new file mode 100644
index 00000000..f4307138
--- /dev/null
+++ b/core/intro-bank-apis.rst
@@ -0,0 +1,134 @@
+################################################
+Introduction to Taler (Core-)Banking Integration
+################################################
+
+This chapter provides an overview of the different ways that a bank or core
+banking system can provide integration with GNU Taler.
+
+
+Settlement Account Access
+#########################
+
+To make a GNU Taler deployment possible, the exchange service needs API access
+to a settlement bank account. The settlement account is used to fund digital
+cash withdrawals into users' Taler wallets, as well as to pay out merchants
+that deposited digital cash with the exchange.
+
+The following two operations need to be supported by the settlement account:
+
+1. Querying transactions on the settlement account.
+2. Initiating payments (simple credit transfer).
+
+Note that there is only **one** settlement account needed per Taler deployment.
+There is no per-user settlement account. Thus, creating or managing bank
+accounts is not a requirement.
+
+A core banking system could directly provide the HTTP/REST APIs
+consumed by the Taler exchange (:doc:`api-bank-integration`). Otherwise, an adapter (typically part
+of the libeufin component) needs to be written.
+
+
+Improved Withdrawal Process
+###########################
+
+In principle, any typical account-based (core-)banking system can be used as
+the underlying account-based financial layer for GNU Taler.
+
+However, without extra support from the bank, withdrawals can be difficult for
+unexperienced users. Thus to make sure that a Taler deployment can achieve
+mass adoption from non-technical users, extra integration by the bank / core
+banking system should be provided.
+
+Withdrawals without any extra support from the core banking system require the
+user to make a transaction to a particular bank account (i.e. the exchange's
+settlement account) with a rather long cryptographic identifier in the subject.
+This identifier is generated by the user's wallet when initiating a withdrawal
+from the wallet application. If the user misspells the identifier, the payment
+will be sent back automatically by the exchange.
+
+However, until the wire transfer is successfully completed, the wallet has no
+information about whether the transfer was already made by the user or if it
+was even made correctly. This makes it hard to show accurate information to
+the user about the status of withdrawing digital cash into their Taler wallet.
+
+
+Withdrawal Bank-Integration API
+===============================
+
+A core banking system can provide better support for GNU Taler withdrawals by
+allowing users to initiate *Taler withdrawal operations* from their bank
+account (e.g. in their banking app or online banking). A Taler withdrawal
+operation has a unique identifier (chosen/generated by the core banking
+system), called the WOPID (Withdrawal Operation Identifier).
+
+The core banking system can provide a (public) API to access withdrawal
+operations (:doc:`api-bank-integration`).
+
+The wallet learns the WOPID and API address via a ``taler://withdraw/...``
+QR-Code or link.
+
+The wallet generates the cryptographic identifiers required for the withdrawal,
+allows the user to choose an exchange (the bank may suggest a default one!),
+and then submits this information (chosen exchange, reserve public key) for the
+respective WOPID to the bank-integration API.
+
+The user can then confirm the withdrawal by completing the 2FA of
+their bank account for the operation. (Alternatively, the user can abort the operation
+in their banking client / 2FA app.)
+
+Upon completion of the 2FA / confirmation step, the core banking system
+initiates a credit transfer for the withdrawal operations (with the chosen
+exchange as the creditor and the cryptographic identifier / reserve pub in the
+subject). Afterwards, the withdrawal operation is marked as done.
+
+The wallet can continuously query the status of the withdrawal operation (via
+the API address and WOPID).
+In the Taler wallet app, the user can now always see the accurate status of
+their withdrawal operation (e.g. bank transfer done, aborted, confirmation/2FA
+pending).
+
+
+Payto-URI Integration
+=====================
+
+When initiating a withdrawal from the Taler wallet, the wallet can generate a
+payto:// link or QR code (see `RFC 8905 <https://www.rfc-editor.org/rfc/rfc8905.txt>`_)
+with the payment information necessary to make a
+credit transfer to the exchange's settlement account.
+
+Banking apps may provide integration here simply by handling payto:// URIs.
+
+Integration based on payto:// URIs prevents mistakes in typing the
+cryptographic identifier and bank account number during withdrawals. However,
+it still does not allow the wallet do accurately show the status of a
+withdrawal to the user.
+
+
+Future: Intent-Based Integration on Android
+===========================================
+
+(This type of integration is currently not specified, but planned for the future.)
+
+On the Android platform, applications can communicate via
+`Intents <https://developer.android.com/guide/components/intents-filters>`_.
+That would allow the Taler wallet to open a credit transfer dialog in a
+supported banking app to fund a withdrawal. The banking app can send the
+status of the credit transfer (confirmed/aborted by the user) back to the
+wallet, which can then show more accurate status information about the
+withdrawal to the user.
+
+
+Integration for Merchants
+#########################
+
+The Taler merchant backend has the option to connect to what
+we call the :doc:`Bank Revenue API <api-bank-revenue>`.
+
+A core banking system may provide this API to merchants that have a business
+account at the that bank. The API provides read-only access to incoming
+transactions on the merchant bank account.
+
+It allows merchants to easily and automatically reconcile incoming bank
+transfers from the Taler exchange's settlement account with the respective
+Taler payments. Usually multiple Taler payments are aggregated into one larger
+payment in the underlying banking layer.
diff --git a/core/tos.rst b/core/tos.rst
index 0cdb8986..6c36be4c 100644
--- a/core/tos.rst
+++ b/core/tos.rst
@@ -29,9 +29,10 @@ and the privacy policy of a service.
the case if the terms of service were not configured correctly.
When returning a full response (not a "304 Not Modified"), the server
- should also include a "Acceptable-Languages" header which includes
- a space-separated list of the languages in which the terms of service
- are available in. Clients can use this to generate a language switcher
+ should also include a "Avail-Languages" header which includes
+ a comma-separated list of the languages in which the terms of service
+ are available in (see `availability hints specification
+ <https://datatracker.ietf.org/doc/draft-nottingham-http-availability-hints/>`_). Clients can use this to generate a language switcher
for users that may not have expressed a proper language preference.