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.rst270
-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)98
-rw-r--r--core/api-bank-wire.rst (renamed from core/api-wire.rst)194
-rw-r--r--core/api-c2ec.rst342
-rw-r--r--core/api-challenger.rst353
-rw-r--r--core/api-common.rst476
-rw-r--r--core/api-corebank.rst1234
-rw-r--r--core/api-donau.rst615
-rw-r--r--core/api-error.rst1205
-rw-r--r--core/api-exchange.rst3619
-rw-r--r--core/api-mailbox.rst215
-rw-r--r--core/api-merchant.rst2304
-rw-r--r--core/api-overview.rst150
-rw-r--r--core/api-sync.rst29
-rw-r--r--core/api-taldir.rst258
-rw-r--r--core/errors.rst79
-rw-r--r--core/index-bank-apis.rst38
-rw-r--r--core/index.rst31
-rw-r--r--core/intro-bank-apis.rst134
-rw-r--r--core/taler-uri.rst149
-rw-r--r--core/tos.rst43
-rw-r--r--core/wireformats.rst62
25 files changed, 8883 insertions, 3648 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 0d1450a8..00000000
--- a/core/api-bank-access.rst
+++ /dev/null
@@ -1,270 +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[]
- }
-
- 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 BankAccountBalanceResponse {
- // Available balance on the account.
- balance: {
- amount: Amount;
- credit_debit_indicator: "credit" | "debit";
- };
- // payto://-URI of the account. (New)
- paytoUri: 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: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 (defaults to 1, meaning the page with the latest transactions.)
- :query size: how many transactions per page, defaults to 5.
-
- **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; // YYYY-MM-DD ending with 'Z'
- }
-
-
-.. 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.
-
-----------------------
-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 2c8f1934..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 from an exchange to the merchant.
+ Return a list of transactions made to an account.
- 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.
@@ -69,7 +94,7 @@ Querying the transaction history
* 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**
+ **Request:**
:query start: *Optional.*
Row identifier to explicitly set the *starting point* of the query.
@@ -82,26 +107,34 @@ Querying the transaction history
the bank may return a response immediately or after waiting only a fraction
of ``long_poll_ms``.
- **Response**
+ **Response:**
- :http:statuscode:`200 OK`: JSON object of type `MerchantIncomingHistory`.
- :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:`200 OK`:
+ JSON object of type `RevenueIncomingHistory`.
+ :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.
- .. 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;
@@ -114,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 63373f68..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.
@@ -144,7 +175,7 @@ Querying the transaction history
* 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**
+ **Request:**
:query start: *Optional.*
Row identifier to explicitly set the *starting point* of the query.
@@ -157,20 +188,33 @@ Querying the transaction history
the bank may return a response immediately or after waiting only a fraction
of ``long_poll_ms``.
- **Response**
+ **Response:**
- :http:statuscode:`200 OK`: JSON object of type `IncomingHistory`.
- :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:`200 OK`:
+ JSON object of type `IncomingHistory`.
+ :http:statuscode:`204 No content`:
+ There are not transactions to report (under the given filter).
+ :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.
+
+ **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
@@ -194,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;
@@ -235,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.
@@ -263,7 +303,7 @@ Querying the transaction history
* 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**
+ **Request:**
:query start: *Optional.*
Row identifier to explicitly set the *starting point* of the query.
@@ -276,26 +316,38 @@ Querying the transaction history
the bank may return a response immediately or after waiting only a fraction
of ``long_poll_ms``.
- **Response**
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ JSON object of type `OutgoingHistory`.
+ :http:statuscode:`204 No content`:
+ There are not transactions to report (under the given filter).
+ :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:`200 OK`: JSON object of type `OutgoingHistory`.
- :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.
+ **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;
@@ -308,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;
@@ -327,26 +375,14 @@ Wire Transfer Test APIs
Endpoints in this section are only used for integration tests and never
exposed by bank gateways in production.
-.. http:post:: ${BASE_URL}/admin/add-incoming
+.. _twg-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
@@ -365,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 b32a7883..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
@@ -117,7 +123,7 @@ handle the error as if an internal error (500) had been returned.
// Name of the object that was bogus (if applicable).
object?: string;
- // Name of the currency than was problematic (if applicable).
+ // Name of the currency that was problematic (if applicable).
currency?: string;
// Expected type (if applicable).
@@ -125,6 +131,9 @@ handle the error as if an internal error (500) had been returned.
// Type that was provided instead (if applicable).
type_actual?: string;
+
+ // Extra information that doesn't fit into the above (if applicable).
+ extra?: Object;
}
-----------------------
@@ -183,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:
----------------
@@ -209,6 +241,7 @@ resulting encoding.
Hash codes
^^^^^^^^^^
+
Hash codes are strings representing base32 encoding of the respective
hashed data. See `base32`_.
@@ -275,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
^^^^^^^^^^
@@ -291,7 +335,7 @@ Timestamps are represented by the following structure:
.. ts:def:: RelativeTime
- interface Duration {
+ interface RelativeTime {
// Duration in microseconds or "forever"
// to represent an infinite duration. Numeric
// values are capped at 2^53 - 1 inclusive.
@@ -310,6 +354,14 @@ Integers
// JavaScript numbers restricted to integers.
type Integer = number;
+Floats
+^^^^^^
+
+.. ts:def:: Float
+
+ // JavaScript numbers.
+ type Float = number;
+
Ages
^^^^
@@ -349,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
^^^^
@@ -421,8 +543,31 @@ Blinded coin
.. ts:def:: CoinEnvelope
- // Blinded coin's `public EdDSA key <eddsa-coin-pub>`, `base32` encoded.
- type CoinEnvelope = string;
+ // The type of a coin's blinded envelope depends on the cipher that is used
+ // for signing with a denomination key.
+ type CoinEnvelope = RSACoinEnvelope | CSCoinEnvelope ;
+
+.. ts:def:: RSACoinEnvelope
+
+ // For denomination signatures based on RSA, the planchet is just a blinded
+ // coin's `public EdDSA key <eddsa-coin-pub>`.
+ interface RSACoinEnvelope {
+ cipher: "RSA" | "RSA+age_restricted";
+ rsa_blinded_planchet: BlindedRsaSignature;
+ }
+
+.. ts:def:: CSCoinEnvelope
+
+ // For denomination signatures based on Blind Clause-Schnorr, the planchet
+ // 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 CSCoinEnvelope {
+ 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:: DenominationBlindingKeyP
@@ -432,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:
@@ -501,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;
@@ -616,7 +786,7 @@ uses 512-bit hash codes (64 bytes).
.. sourcecode:: c
- struct TALER_ExtensionContractHash {
+ struct TALER_ExtensionsPolicyHash {
struct GNUNET_HashCode hash;
};
@@ -633,6 +803,7 @@ uses 512-bit hash codes (64 bytes).
struct GNUNET_ShortHashCode hash;
};
+.. _BlindedCoinHash:
.. sourcecode:: c
struct TALER_BlindedCoinHash {
@@ -645,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
@@ -690,6 +868,28 @@ uses 512-bit hash codes (64 bytes).
uint8_t ecdhe_priv[32];
};
+
+.. _AmlDecisionState:
+.. sourcecode:: c
+
+ enum TALER_AmlDecisionState {
+ NORMAL, PENDING, FROZEN
+ };
+
+.. _AmlOfficerPublicKeyP:
+.. sourcecode:: c
+
+ struct TALER_AmlOfficerPublicKeyP {
+ uint8_t eddsa_pub[32];
+ };
+
+.. _AmlOfficerPrivateKeyP:
+.. sourcecode:: c
+
+ struct TALER_AmlOfficerPrivateKeyP {
+ uint8_t eddsa_priv[32];
+ };
+
.. _sign-key-pub:
.. sourcecode:: c
@@ -774,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
@@ -792,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
@@ -802,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;
};
@@ -830,7 +1036,40 @@ within the
struct TALER_BlindedCoinHash h_coin_envelope;
};
-.. _TALER_DepositRequestPS:
+.. _TALER_AgeWithdrawRequestPS:
+.. sourcecode:: c
+
+ struct TALER_AgeWithdrawRequestPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ /**
+ * 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:
+
.. sourcecode:: c
struct TALER_DepositRequestPS {
@@ -839,7 +1078,8 @@ within the
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_PrivateContractHash h_contract_terms;
- struct TALER_ExtensionContractHash h_extensions;
+ struct TALER_AgeCommitmentHash h_age_commitment;
+ struct TALER_ExtensionsPolicyHash h_policy;
struct TALER_MerchantWireHash h_wire;
struct TALER_DenominationHash h_denom_pub;
struct GNUNET_TIME_AbsoluteNBO timestamp;
@@ -847,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 {
@@ -859,7 +1105,7 @@ within the
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_PrivateContractHash h_contract_terms;
struct TALER_MerchantWireHash h_wire;
- struct TALER_ExtensionContractHash h_extensions;
+ struct TALER_ExtensionsPolicyHash h_policy;
struct GNUNET_TIME_AbsoluteNBO timestamp;
struct GNUNET_TIME_AbsoluteNBO refund_deadline;
struct TALER_AmountNBO amount_without_fee;
@@ -877,6 +1123,7 @@ within the
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_RefreshCommitmentP session_hash;
struct TALER_DenominationHash h_denom_pub;
+ struct TALER_AgeCommitmentHash h_age_commitment;
struct TALER_AmountNBO amount_with_fee;
struct TALER_AmountNBO melt_fee;
};
@@ -901,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;
@@ -949,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:
@@ -966,6 +1215,41 @@ within the
struct TALER_AmountNBO closing_fee;
};
+.. _TALER_GlobalFeesPS:
+.. sourcecode:: c
+
+ struct TALER_GlobalFeesPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_MASTER_GLOBAL_FEES
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_TIME_AbsoluteNBO start_date;
+ struct GNUNET_TIME_AbsoluteNBO end_date;
+ struct GNUNET_TIME_RelativeNBO purse_timeout;
+ struct GNUNET_TIME_RelativeNBO kyc_timeout;
+ struct GNUNET_TIME_RelativeNBO history_expiration;
+ struct TALER_AmountNBO history_fee;
+ struct TALER_AmountNBO kyc_fee;
+ struct TALER_AmountNBO account_fee;
+ struct TALER_AmountNBO purse_fee;
+ uint32_t purse_account_limit;
+ };
+
+.. _TALER_MasterDrainProfitPS:
+.. sourcecode:: c
+
+ struct TALER_MasterDrainProfitPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_MASTER_DRAIN_PROFITS
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct GNUNET_TIME_AbsoluteNBO date;
+ struct TALER_AmountNBO amount;
+ struct GNUNET_HashCode h_section;
+ struct TALER_PaytoHashP h_payto;
+ };
+
.. _TALER_DepositTrackPS:
.. sourcecode:: c
@@ -976,7 +1260,6 @@ within the
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_PrivateContractHash h_contract_terms;
struct TALER_MerchantWireHash h_wire;
- struct TALER_MerchantPublicKeyP merchant;
union TALER_CoinSpendPublicKeyP coin_pub;
};
@@ -1047,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:
@@ -1322,7 +1602,23 @@ within the
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_AmountNBO coin_contribution;
+ struct TALER_DenominationHash h_denom_pub;
+ struct TALER_AgeCommitmentHash h_age_commitment;
struct TALER_PursePublicKeyP purse_pub;
+ struct GNUNET_HashCode h_exchange_base_url;
+ };
+
+
+.. _TALER_ReserveOpenDepositSignaturePS:
+.. sourcecode:: c
+
+ struct TALER_PurseDepositSignaturePS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_ReserveSignatureP reserve_sig;
+ struct TALER_AmountNBO coin_contribution;
};
@@ -1371,6 +1667,30 @@ within the
uint32_t min_age;
};
+.. _TALER_AccountSetupRequestSignaturePS:
+.. sourcecode:: c
+
+ struct TALER_AccountSetupRequestSignaturePS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_WALLET_ACCOUNT_SETUP
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_AmountNBO threshold;
+ };
+
+.. _TALER_AccountSetupSuccessSignaturePS:
+.. sourcecode:: c
+
+ struct TALER_AccountSetupSuccessSignaturePS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_WALLET_ACCOUNT_SETUP_SUCCESS
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_PaytoHash h_payto;
+ struct GNUNET_HashCode h_kyc;
+ struct GNUNET_TIME_AbsoluteNBO timestamp;
+ };
+
.. _TALER_PurseMergeSuccessSignaturePS:
.. sourcecode:: c
@@ -1451,9 +1771,7 @@ within the
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
struct TALER_PursePublicKey purse_pub;
union TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_MerchantPublicKeyP merchant;
- struct TALER_AmountNBO remaining_amount;
- struct TALER_AmountNBO purse_fee_share;
+ struct TALER_AmountNBO refunded_amount;
struct TALER_AmountNBO refund_fee;
};
@@ -1545,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:
@@ -1558,3 +1879,104 @@ within the
struct GNUNET_TIME_AbsoluteNBO end_date;
struct TALER_PaytoHash h_wire;
};
+
+
+.. _TALER_MasterAmlOfficerStatusPS:
+.. sourcecode:: c
+
+ struct TALER_MasterAmlOfficerStatusPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_MASTER_AML_KEY
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_TIME_TimestampNBO change_date;
+ struct TALER_AmlOfficerPublicKeyP officer_pub;
+ struct GNUNET_HashCode h_officer_name GNUNET_PACKED;
+ 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
+
+ struct TALER_PartnerConfigurationPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_MASTER_PARNTER_DETAILS
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_MasterPublicKeyP partner_pub;
+ struct GNUNET_TIME_TimestampNBO start_date;
+ struct GNUNET_TIME_TimestampNBO end_date;
+ struct GNUNET_TIME_RelativeNBO wad_frequency;
+ struct TALER_AmountNBO wad_fee;
+ struct GNUNET_HashCode h_url;
+ };
+
+.. _TALER_ReserveOpenPS:
+.. sourcecode:: c
+
+ struct TALER_ReserveOpenPS {
+ /**
+ * Purpose.purpose = TALER_SIGNATURE_WALLET_RESERVE_OPEN
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct TALER_AmountNBO reserve_payment;
+ struct GNUNET_TIME_TimestampNBO request_timestamp;
+ struct GNUNET_TIME_TimestampNBO reserve_expiration;
+ uint32_t purse_limit;
+ };
+
+.. _TALER_ReserveClosePS:
+.. sourcecode:: c
+
+ struct TALER_ReserveClosePS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_WALLET_RESERVE_CLOSE
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_TIME_TimestampNBO request_timestamp;
+ struct TALER_PaytoHashP target_account_h_payto;
+ };
+
+.. _TALER_WalletReserveAttestRequestSignaturePS:
+.. sourcecode:: c
+
+ struct TALER_ReserveAttestRequestPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_WALLET_ATTEST_REQUEST
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_TIME_TimestampNBO request_timestamp;
+ struct GNUNET_HashCode h_details;
+ };
+
+.. _TALER_ExchangeAttestPS:
+.. sourcecode:: c
+
+ struct TALER_ExchangeAttestPS {
+ /**
+ * purpose.purpose = TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+ struct GNUNET_TIME_TimestampNBO attest_timestamp;
+ 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-error.rst b/core/api-error.rst
deleted file mode 100644
index 6015579b..00000000
--- a/core/api-error.rst
+++ /dev/null
@@ -1,1205 +0,0 @@
-..
- This file is part of GNU TALER.
- Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
- 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
-
-..
- The reason to have a dedicated page for error codes was due to a buggy
- behaviour in pages cross-linking: was not possible from other pages to
- reference the '_error-codes' label (see just below) if we kept in api-common.rst
- (which is the best place to place this error codes list).
-
------------
-Error Codes
------------
-
-The following list shows error codes defined in
-`<EXCHANGE-REPO>/src/include/taler_error_codes.h <https://git.taler.net/exchange.git/tree/src/include/taler_error_codes.h>`_.
-
-.. _error-codes:
-.. _TALER_ErrorCode:
-.. code-block:: c
-
- /**
- * Enumeration with all possible Taler error codes.
- */
- enum TALER_ErrorCode {
-
- /**
- * Special code to indicate no error (or no "code" present).
- */
- TALER_EC_NONE = 0,
-
- /**
- * Special code to indicate that a non-integer error code was
- * returned in the JSON response.
- */
- TALER_EC_INVALID = 1,
-
- /**
- * The response we got from the server was not even in JSON format.
- */
- TALER_EC_INVALID_RESPONSE = 2,
-
- /**
- * Generic implementation error: this function was not yet implemented.
- */
- TALER_EC_NOT_IMPLEMENTED = 3,
-
- /* ********** generic error codes ************* */
-
- /**
- * The exchange failed to even just initialize its connection to the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DB_SETUP_FAILED = 1001,
-
- /**
- * The exchange encountered an error event to just start
- * the database transaction.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DB_START_FAILED = 1002,
-
- /**
- * The exchange encountered an error event to commit
- * the database transaction (hard, unrecoverable error).
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DB_COMMIT_FAILED_HARD = 1003,
-
- /**
- * The exchange encountered an error event to commit
- * the database transaction, even after repeatedly
- * retrying it there was always a conflicting transaction.
- * (This indicates a repeated serialization error; should
- * only happen if some client maliciously tries to create
- * conflicting concurrent transactions.)
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DB_COMMIT_FAILED_ON_RETRY = 1004,
-
- /**
- * The exchange had insufficient memory to parse the request. This
- * response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_PARSER_OUT_OF_MEMORY = 1005,
-
- /**
- * The JSON in the client's request to the exchange was malformed.
- * (Generic parse error).
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_JSON_INVALID = 1006,
-
- /**
- * The JSON in the client's request to the exchange was malformed.
- * Details about the location of the parse error are provided.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_JSON_INVALID_WITH_DETAILS = 1007,
-
- /**
- * A required parameter in the request to the exchange was missing.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PARAMETER_MISSING = 1008,
-
- /**
- * A parameter in the request to the exchange was malformed.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PARAMETER_MALFORMED = 1009,
-
- /* ********** request-specific error codes ************* */
-
- /**
- * The given reserve does not have sufficient funds to admit the
- * requested withdraw operation at this time. The response includes
- * the current "balance" of the reserve as well as the transaction
- * "history" that led to this balance. This response is provided
- * with HTTP status code MHD_HTTP_FORBIDDEN.
- */
- TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS = 1100,
-
- /**
- * The exchange has no information about the "reserve_pub" that
- * was given.
- * This response is provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_WITHDRAW_RESERVE_UNKNOWN = 1101,
-
- /**
- * The amount to withdraw together with the fee exceeds the
- * numeric range for Taler amounts. This is not a client
- * failure, as the coin value and fees come from the exchange's
- * configuration.
- * This response is provided with HTTP status code MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW = 1102,
-
- /**
- * All of the deposited amounts into this reserve total up to a
- * value that is too big for the numeric range for Taler amounts.
- * This is not a client failure, as the transaction history comes
- * from the exchange's configuration. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW = 1103,
-
- /**
- * For one of the historic withdrawals from this reserve, the
- * exchange could not find the denomination key.
- * This is not a client failure, as the transaction history comes
- * from the exchange's configuration. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND = 1104,
-
- /**
- * All of the withdrawals from reserve total up to a
- * value that is too big for the numeric range for Taler amounts.
- * This is not a client failure, as the transaction history comes
- * from the exchange's configuration. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW = 1105,
-
- /**
- * The exchange somehow knows about this reserve, but there seems to
- * have been no wire transfers made. This is not a client failure,
- * as this is a database consistency issue of the exchange. This
- * response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER = 1106,
-
- /**
- * The exchange failed to create the signature using the
- * denomination key. This response is provided with HTTP status
- * code MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_SIGNATURE_FAILED = 1107,
-
- /**
- * The exchange failed to store the withdraw operation in its
- * database. This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_DB_STORE_ERROR = 1108,
-
- /**
- * The exchange failed to check against historic withdraw data from
- * the database (as part of ensuring the idempotency of the operation).
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_WITHDRAW_DB_FETCH_ERROR = 1109,
-
- /**
- * The exchange is not aware of the denomination key
- * the wallet requested for the withdrawal.
- * This response is provided
- * with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND = 1110,
-
- /**
- * The signature of the reserve is not valid. This response is
- * provided with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_WITHDRAW_RESERVE_SIGNATURE_INVALID = 1111,
-
- /**
- * The exchange failed to obtain the transaction history of the
- * given reserve from the database while generating an insufficient
- * funds error.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1112,
-
- /**
- * When computing the reserve history, we ended up with a negative
- * overall balance, which should be impossible.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE = 1113,
-
- /**
- * The exchange failed to obtain the transaction history of the
- * given reserve from the database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_RESERVE_STATUS_DB_ERROR = 1150,
-
-
- /**
- * The respective coin did not have sufficient residual value
- * for the /deposit operation (i.e. due to double spending).
- * The "history" in the response provides the transaction history
- * of the coin proving this fact. This response is provided
- * with HTTP status code MHD_HTTP_FORBIDDEN.
- */
- TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS = 1200,
-
- /**
- * The exchange failed to obtain the transaction history of the
- * given coin from the database (this does not happen merely because
- * the coin is seen by the exchange for the first time).
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSIT_HISTORY_DB_ERROR = 1201,
-
- /**
- * The exchange failed to store the /depost information in the
- * database. This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSIT_STORE_DB_ERROR = 1202,
-
- /**
- * The exchange database is unaware of the denomination key that
- * signed the coin (however, the exchange process is; this is not
- * supposed to happen; it can happen if someone decides to purge the
- * DB behind the back of the exchange process). Hence the deposit
- * is being refused. This response is provided with HTTP status
- * code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN = 1203,
-
- /**
- * The exchange database is unaware of the denomination key that
- * signed the coin (however, the exchange process is; this is not
- * supposed to happen; it can happen if someone decides to purge the
- * DB behind the back of the exchange process). Hence the deposit
- * is being refused. This response is provided with HTTP status
- * code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_DEPOSIT_DENOMINATION_KEY_UNKNOWN = 1204,
-
- /**
- * The signature of the coin is not valid. This response is
- * provided with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_COIN_SIGNATURE_INVALID = 1205,
-
- /**
- * The signature of the denomination key over the coin is not valid.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_DENOMINATION_SIGNATURE_INVALID = 1206,
-
- /**
- * The stated value of the coin after the deposit fee is subtracted
- * would be negative.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE = 1207,
-
- /**
- * The stated refund deadline is after the wire deadline.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE = 1208,
-
- /**
- * The exchange does not recognize the validity of or support the
- * given wire format type.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE = 1209,
-
- /**
- * The exchange failed to canonicalize and hash the given wire format.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_JSON = 1210,
-
- /**
- * The hash of the given wire address does not match the hash
- * specified in the contract.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_CONTRACT_HASH_CONFLICT = 1211,
-
- /**
- * The exchange failed to obtain the transaction history of the
- * given coin from the database while generating an insufficient
- * funds error.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSIT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1212,
-
- /**
- * The exchange detected that the given account number
- * is invalid for the selected wire format type.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER = 1213,
-
- /**
- * The signature over the given wire details is invalid.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE = 1214,
-
- /**
- * The bank specified in the wire transfer format is not supported
- * by this exchange.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_BANK = 1215,
-
- /**
- * No wire format type was specified in the JSON wire format
- * details.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_MISSING = 1216,
-
- /**
- * The given wire format type is not supported by this
- * exchange.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_UNSUPPORTED = 1217,
-
-
- /**
- * The respective coin did not have sufficient residual value
- * for the /refresh/melt operation. The "history" in this
- * response provdes the "residual_value" of the coin, which may
- * be less than its "original_value". This response is provided
- * with HTTP status code MHD_HTTP_FORBIDDEN.
- */
- TALER_EC_MELT_INSUFFICIENT_FUNDS = 1300,
-
- /**
- * The exchange is unaware of the denomination key that was
- * used to sign the melted coin. This response is provided
- * with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_MELT_DENOMINATION_KEY_NOT_FOUND = 1301,
-
- /**
- * The exchange had an internal error reconstructing the
- * transaction history of the coin that was being melted.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_MELT_COIN_HISTORY_COMPUTATION_FAILED = 1302,
-
- /**
- * The exchange failed to check against historic melt data from the
- * database (as part of ensuring the idempotency of the operation).
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_MELT_DB_FETCH_ERROR = 1303,
-
- /**
- * The exchange failed to store session data in the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_MELT_DB_STORE_SESSION_ERROR = 1304,
-
- /**
- * The exchange failed to store refresh order data in the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_MELT_DB_STORE_ORDER_ERROR = 1305,
-
- /**
- * The exchange failed to store commit data in the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_MELT_DB_STORE_COMMIT_ERROR = 1306,
-
- /**
- * The exchange failed to store transfer keys in the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_MELT_DB_STORE_TRANSFER_ERROR = 1307,
-
- /**
- * The exchange is unaware of the denomination key that was
- * requested for one of the fresh coins. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND = 1308,
-
- /**
- * The exchange encountered a numeric overflow totaling up
- * the cost for the refresh operation. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_MELT_COST_CALCULATION_OVERFLOW = 1309,
-
- /**
- * During the transaction phase, the exchange could suddenly
- * no longer find the denomination key that was
- * used to sign the melted coin. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_MELT_DB_DENOMINATION_KEY_NOT_FOUND = 1310,
-
- /**
- * The exchange encountered melt fees exceeding the melted
- * coin's contribution. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_FEES_EXCEED_CONTRIBUTION = 1311,
-
- /**
- * The exchange's cost calculation does not add up to the
- * melt fees specified in the request. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_FEES_MISSMATCH = 1312,
-
- /**
- * The denomination key signature on the melted coin is invalid.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_DENOMINATION_SIGNATURE_INVALID = 1313,
-
- /**
- * The exchange's cost calculation shows that the melt amount
- * is below the costs of the transaction. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_AMOUNT_INSUFFICIENT = 1314,
-
- /**
- * The signature made with the coin to be melted is invalid.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_COIN_SIGNATURE_INVALID = 1315,
-
- /**
- * The size of the cut-and-choose dimension of the
- * blinded coins request does not match #TALER_CNC_KAPPA.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_CNC_COIN_ARRAY_SIZE_INVALID = 1316,
-
- /**
- * The size of the cut-and-choose dimension of the
- * transfer keys request does not match #TALER_CNC_KAPPA.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1317,
-
- /**
- * The exchange failed to obtain the transaction history of the
- * given coin from the database while generating an insufficient
- * funds error.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1318,
-
- /**
- * The provided transfer keys do not match up with the
- * original commitment. Information about the original
- * commitment is included in the response. This response is
- * provided with HTTP status code MHD_HTTP_CONFLICT.
- */
- TALER_EC_REVEAL_COMMITMENT_VIOLATION = 1350,
-
- /**
- * Failed to blind the envelope to reconstruct the blinded
- * coins for revelation checks.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REVEAL_BLINDING_ERROR = 1351,
-
- /**
- * Failed to produce the blinded signatures over the coins
- * to be returned.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REVEAL_SIGNING_ERROR = 1352,
-
- /**
- * The exchange is unaware of the refresh session specified in
- * the request.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_REVEAL_SESSION_UNKNOWN = 1353,
-
- /**
- * The exchange failed to retrieve valid session data from the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REVEAL_DB_FETCH_SESSION_ERROR = 1354,
-
- /**
- * The exchange failed to retrieve order data from the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REVEAL_DB_FETCH_ORDER_ERROR = 1355,
-
- /**
- * The exchange failed to retrieve transfer keys from the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REVEAL_DB_FETCH_TRANSFER_ERROR = 1356,
-
- /**
- * The exchange failed to retrieve commitment data from the
- * database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_ERROR.
- */
- TALER_EC_REVEAL_DB_FETCH_COMMIT_ERROR = 1357,
-
- /**
- * The size of the cut-and-choose dimension of the
- * private transfer keys request does not match #TALER_CNC_KAPPA - 1.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1358,
-
-
- /**
- * The coin specified in the link request is unknown to the exchange.
- * This response is provided with HTTP status code
- * MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_LINK_COIN_UNKNOWN = 1400,
-
-
- /**
- * The exchange knows literally nothing about the coin we were asked
- * to refund. But without a transaction history, we cannot issue a
- * refund. This is kind-of OK, the owner should just refresh it
- * directly without executing the refund. This response is provided
- * with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_REFUND_COIN_NOT_FOUND = 1500,
-
- /**
- * We could not process the refund request as the coin's transaction
- * history does not permit the requested refund at this time. The
- * "history" in the response proves this. This response is provided
- * with HTTP status code MHD_HTTP_CONFLICT.
- */
- TALER_EC_REFUND_CONFLICT = 1501,
-
- /**
- * The exchange knows about the coin we were asked to refund, but
- * not about the specific /deposit operation. Hence, we cannot
- * issue a refund (as we do not know if this merchant public key is
- * authorized to do a refund). This response is provided with HTTP
- * status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_REFUND_DEPOSIT_NOT_FOUND = 1503,
-
- /**
- * The currency specified for the refund is different from
- * the currency of the coin. This response is provided with HTTP
- * status code MHD_HTTP_PRECONDITION_FAILED.
- */
- TALER_EC_REFUND_CURRENCY_MISSMATCH = 1504,
-
- /**
- * When we tried to check if we already paid out the coin, the
- * exchange's database suddenly disagreed with data it previously
- * provided (internal inconsistency).
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_REFUND_DB_INCONSISTENT = 1505,
-
- /**
- * The exchange can no longer refund the customer/coin as the
- * money was already transferred (paid out) to the merchant.
- * (It should be past the refund deadline.)
- * This response is provided with HTTP status code
- * MHD_HTTP_GONE.
- */
- TALER_EC_REFUND_MERCHANT_ALREADY_PAID = 1506,
-
- /**
- * The amount the exchange was asked to refund exceeds
- * (with fees) the total amount of the deposit (including fees).
- * This response is provided with HTTP status code
- * MHD_HTTP_PRECONDITION_FAILED.
- */
- TALER_EC_REFUND_INSUFFICIENT_FUNDS = 1507,
-
- /**
- * The exchange failed to recover information about the
- * denomination key of the refunded coin (even though it
- * recognizes the key). Hence it could not check the fee
- * structure.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND = 1508,
-
- /**
- * The refund fee specified for the request is lower than
- * the refund fee charged by the exchange for the given
- * denomination key of the refunded coin.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_REFUND_FEE_TOO_LOW = 1509,
-
- /**
- * The exchange failed to store the refund information to
- * its database.
- * This response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_REFUND_STORE_DB_ERROR = 1510,
-
- /**
- * The refund fee is specified in a different currency
- * than the refund amount.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_REFUND_FEE_CURRENCY_MISSMATCH = 1511,
-
- /**
- * The refunded amount is smaller than the refund fee,
- * which would result in a negative refund.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_REFUND_FEE_ABOVE_AMOUNT = 1512,
-
- /**
- * The signature of the merchant is invalid.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID = 1513,
-
-
- /**
- * The wire format specified in the "sender_account_details"
- * is not understood or not supported by this exchange.
- * Returned with an HTTP status code of MHD_HTTP_NOT_FOUND.
- * (As we did not find an interpretation of the wire format.)
- */
- TALER_EC_ADMIN_ADD_INCOMING_WIREFORMAT_UNSUPPORTED = 1600,
-
- /**
- * The currency specified in the "amount" parameter is not
- * supported by this exchange. Returned with an HTTP status
- * code of MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_ADMIN_ADD_INCOMING_CURRENCY_UNSUPPORTED = 1601,
-
- /**
- * The exchange failed to store information about the incoming
- * transfer in its database. This response is provided with HTTP
- * status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_ADMIN_ADD_INCOMING_DB_STORE = 1602,
-
- /**
- * The exchange encountered an error (that is not about not finding
- * the wire transfer) trying to look up a wire transfer identifier
- * in the database. This response is provided with HTTP
- * status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_DB_FETCH_FAILED = 1700,
-
- /**
- * The exchange found internally inconsistent data when resolving a
- * wire transfer identifier in the database. This response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_DB_INCONSISTENT = 1701,
-
- /**
- * The exchange did not find information about the specified
- * wire transfer identifier in the database. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_TRANSFERS_GET_WTID_NOT_FOUND = 1702,
-
-
- /**
- * The exchange found internally inconsistent fee data when
- * resolving a transaction in the database. This
- * response is provided with HTTP status code
- * MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSITS_GET_DB_FEE_INCONSISTENT = 1800,
-
- /**
- * The exchange encountered an error (that is not about not finding
- * the transaction) trying to look up a transaction
- * in the database. This response is provided with HTTP
- * status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSITS_GET_DB_FETCH_FAILED = 1801,
-
- /**
- * The exchange did not find information about the specified
- * transaction in the database. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_DEPOSITS_GET_NOT_FOUND = 1802,
-
- /**
- * The exchange failed to identify the wire transfer of the
- * transaction (or information about the plan that it was supposed
- * to still happen in the future). This response is provided with
- * HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSITS_GET_WTID_RESOLUTION_ERROR = 1803,
-
- /**
- * The signature of the merchant is invalid.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID = 1804,
-
-
- /* *********** Merchant backend error codes ********* */
-
- /**
- * The backend could not find the merchant instance specified
- * in the request. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_CONTRACT_INSTANCE_UNKNOWN = 2000,
-
- /**
- * The exchange failed to provide a meaningful response
- * to a /deposit request. This response is provided
- * with HTTP status code MHD_HTTP_SERVICE_UNAVAILABLE.
- */
- TALER_EC_PAY_EXCHANGE_FAILED = 2101,
-
- /**
- * The merchant failed to commit the exchange's response to
- * a /deposit request to its database. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_PAY_DB_STORE_PAY_ERROR = 2102,
-
- /**
- * The specified exchange is not supported/trusted by
- * this merchant. This response is provided
- * with HTTP status code MHD_HTTP_PRECONDITION_FAILED.
- */
- TALER_EC_PAY_EXCHANGE_REJECTED = 2103,
-
- /**
- * The denomination key used for payment is not listed among the
- * denomination keys of the exchange. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_DENOMINATION_KEY_NOT_FOUND = 2104,
-
- /**
- * The denomination key used for payment is not audited by an
- * auditor approved by the merchant. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_DENOMINATION_KEY_AUDITOR_FAILURE = 2105,
-
- /**
- * There was an integer overflow totaling up the amounts or
- * deposit fees in the payment. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_AMOUNT_OVERFLOW = 2106,
-
- /**
- * The deposit fees exceed the total value of the payment.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_FEES_EXCEED_PAYMENT = 2107,
-
- /**
- * After considering deposit fees, the payment is insufficient
- * to satisfy the required amount for the contract.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_PAYMENT_INSUFFICIENT_DUE_TO_FEES = 2108,
-
- /**
- * While the merchant is happy to cover all applicable deposit fees,
- * the payment is insufficient to satisfy the required amount for
- * the contract. This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_PAYMENT_INSUFFICIENT = 2109,
-
- /**
- * The signature over the contract of one of the coins
- * was invalid. This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_COIN_SIGNATURE_INVALID = 2110,
-
- /**
- * We failed to contact the exchange for the /pay request.
- * This response is provided
- * with HTTP status code MHD_HTTP_SERVICE_UNAVAILABLE.
- */
- TALER_EC_PAY_EXCHANGE_TIMEOUT = 2111,
-
- /**
- * The backend could not find the merchant instance specified
- * in the request. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_PAY_INSTANCE_UNKNOWN = 2112,
-
- /**
- * The signature over the contract of the merchant
- * was invalid. This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_MERCHANT_SIGNATURE_INVALID = 2113,
-
- /**
- * The refund deadline was after the transfer deadline.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE = 2114,
-
- /**
- * The request fails to provide coins for the payment.
- * This response is provided with HTTP status code
- * MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_COINS_ARRAY_EMPTY = 2115,
-
- /**
- * The merchant failed to fetch the merchant's previous state with
- * respect to a /pay request from its database. This response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_PAY_DB_FETCH_PAY_ERROR = 2116,
-
- /**
- * The merchant failed to fetch the merchant's previous state with
- * respect to transactions from its database. This response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_PAY_DB_FETCH_TRANSACTION_ERROR = 2117,
-
- /**
- * The transaction ID was used for a conflicing transaction before.
- * This response is
- * provided with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_DB_TRANSACTION_ID_CONFLICT = 2118,
-
- /**
- * The merchant failed to store the merchant's state with
- * respect to the transaction in its database. This response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_PAY_DB_STORE_TRANSACTION_ERROR = 2119,
-
- /**
- * The exchange failed to provide a valid response to
- * the merchant's /keys request.
- * This response is provided
- * with HTTP status code MHD_HTTP_SERVICE_UNAVAILABLE.
- */
- TALER_EC_PAY_EXCHANGE_KEYS_FAILURE = 2120,
-
- /**
- * The payment is too late, the offer has expired.
- * This response is
- * provided with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_PAY_OFFER_EXPIRED = 2121,
-
-
- /**
- * Integer overflow with specified timestamp argument detected.
- * This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_HISTORY_TIMESTAMP_OVERFLOW = 2200,
-
- /**
- * Failed to retrieve history from merchant database.
- * This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_HISTORY_DB_FETCH_ERROR = 2201,
-
- /**
- * We failed to contact the exchange for the /track/transaction
- * request. This response is provided with HTTP status code
- * MHD_HTTP_SERVICE_UNAVAILABLE.
- */
- TALER_EC_DEPOSITS_GET_EXCHANGE_TIMEOUT = 2300,
-
- /**
- * The backend could not find the merchant instance specified
- * in the request. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_DEPOSITS_GET_INSTANCE_UNKNOWN = 2301,
-
- /**
- * The backend could not find the transaction specified
- * in the request. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_DEPOSITS_GET_TRANSACTION_UNKNOWN = 2302,
-
- /**
- * The backend had a database access error trying to
- * retrieve transaction data from its database.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSITS_GET_DB_FETCH_TRANSACTION_ERROR = 2303,
-
- /**
- * The backend had a database access error trying to
- * retrieve payment data from its database.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_DEPOSITS_GET_DB_FETCH_PAYMENT_ERROR = 2304,
-
- /**
- * The backend found no applicable deposits in the database.
- * This is odd, as we know about the transaction, but not
- * about deposits we made for the transaction. The response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_DEPOSITS_GET_DB_NO_DEPOSITS_ERROR = 2305,
-
- /**
- * We failed to obtain a wire transfer identifier for one
- * of the coins in the transaction. The response is
- * provided with HTTP status code MHD_HTTP_FAILED_DEPENDENCY if
- * the exchange had a hard error, or MHD_HTTP_ACCEPTED if the
- * exchange signaled that the transfer was in progress.
- */
- TALER_EC_DEPOSITS_GET_COIN_TRACE_ERROR = 2306,
-
- /**
- * We failed to obtain the full wire transfer identifier for the
- * transfer one of the coins was aggregated into.
- * The response is
- * provided with HTTP status code MHD_HTTP_FAILED_DEPENDENCY.
- */
- TALER_EC_DEPOSITS_GET_WIRE_TRANSFER_TRACE_ERROR = 2307,
-
- /**
- * We got conflicting reports from the exchange with
- * respect to which transfers are included in which
- * aggregate.
- * The response is
- * provided with HTTP status code MHD_HTTP_FAILED_DEPENDENCY.
- */
- TALER_EC_DEPOSITS_GET_CONFLICTING_REPORTS = 2308,
-
-
- /**
- * We failed to contact the exchange for the /track/transfer
- * request. This response is provided with HTTP status code
- * MHD_HTTP_SERVICE_UNAVAILABLE.
- */
- TALER_EC_TRANSFERS_GET_EXCHANGE_TIMEOUT = 2400,
-
- /**
- * The backend could not find the merchant instance specified
- * in the request. This response is
- * provided with HTTP status code MHD_HTTP_NOT_FOUND.
- */
- TALER_EC_TRANSFERS_GET_INSTANCE_UNKNOWN = 2401,
-
- /**
- * We failed to persist coin wire transfer information in
- * our merchant database.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_DB_STORE_COIN_ERROR = 2402,
-
- /**
- * We internally failed to execute the /track/transfer request.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_REQUEST_ERROR = 2403,
-
- /**
- * We failed to persist wire transfer information in
- * our merchant database.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_DB_STORE_TRANSFER_ERROR = 2404,
-
- /**
- * The exchange returned an error from /track/transfer.
- * The response is
- * provided with HTTP status code MHD_HTTP_FAILED_DEPENDENCY.
- */
- TALER_EC_TRANSFERS_GET_EXCHANGE_ERROR = 2405,
-
- /**
- * We failed to fetch deposit information from
- * our merchant database.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_DB_FETCH_DEPOSIT_ERROR = 2406,
-
- /**
- * We encountered an internal logic error.
- * The response is
- * provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_DB_INTERNAL_LOGIC_ERROR = 2407,
-
- /**
- * The exchange gave conflicting information about a coin which has
- * been wire transferred.
- * The response is provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TRANSFERS_GET_CONFLICTING_REPORTS = 2408,
-
- /**
- * The hash provided in the request of /map/in does not match
- * the contract sent alongside in the same request.
- */
- TALER_EC_MAP_IN_UNMATCHED_HASH = 2500,
-
- /**
- * The backend encountered an error while trying to store the
- * pair <contract, h_proposal_data> into the database.
- * The response is provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_MAP_IN_STORE_DB_ERROR = 2501,
-
- /**
- * The backend encountered an error while trying to retrieve the
- * contract from the database. Likely to be an internal error.
- */
- TALER_EC_MAP_OUT_GET_FROM_DB_ERROR = 2502,
-
-
- /**
- * The backend encountered an error while trying to retrieve the
- * contract from the database. Likely to be an internal error.
- */
- TALER_EC_MAP_OUT_CONTRACT_UNKNOWN = 2503,
-
- /* ********** /test API error codes ************* */
-
- /**
- * The exchange failed to compute ECDH. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TEST_ECDH_ERROR = 4000,
-
- /**
- * The EdDSA test signature is invalid. This response is provided
- * with HTTP status code MHD_HTTP_BAD_REQUEST.
- */
- TALER_EC_TEST_EDDSA_INVALID = 4001,
-
- /**
- * The exchange failed to compute the EdDSA test signature. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TEST_EDDSA_ERROR = 4002,
-
- /**
- * The exchange failed to generate an RSA key. This response is provided
- * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TEST_RSA_GEN_ERROR = 4003,
-
- /**
- * The exchange failed to compute the public RSA key. This response
- * is provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TEST_RSA_PUB_ERROR = 4004,
-
- /**
- * The exchange failed to compute the RSA signature. This response
- * is provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
- */
- TALER_EC_TEST_RSA_SIGN_ERROR = 4005,
-
-
- /**
- * End of error code range.
- */
- TALER_EC_END = 9999
- };
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
index b55fb6be..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-2022 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,50 +24,12 @@ 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.
-.. _keys:
-
---------------------
-Terms of service API
---------------------
-
-These APIs allow merchants and wallets to obtain the terms of service
-and the privacy policy of an exchange.
-
-
-.. http:get:: /terms
-
- Get the terms of service of the exchange.
- The exchange will consider the "Accept" and "Accept-Language" and
- "Accept-Encoding" headers when generating a response. Specifically,
- it will try to find a response with an acceptable mime-type, then
- pick the version in the most preferred language of the user, and
- finally apply compression if that is allowed by the client and
- deemed beneficial.
-
- The exchange will set an "Etag", and subsequent requests of the
- same client should provide the tag in an "If-None-Match" header
- to detect if the terms of service have changed. If not, a
- "204 Not Modified" response will be returned.
-
- If the "Etag" is missing, the client should not cache the response and instead prompt the user again at the next opportunity. This is usually only the case if the terms of service were not configured correctly.
+.. contents:: Table of Contents
+ :local:
+.. include:: tos.rst
-.. http:get:: /privacy
-
- Get the privacy policy of the exchange.
- The exchange will consider the "Accept" and "Accept-Language" and
- "Accept-Encoding" headers when generating a response. Specifically,
- it will try to find a response with an acceptable mime-type, then
- pick the version in the most preferred language of the user, and
- finally apply compression if that is allowed by the client and
- deemed beneficial.
-
- The exchange will set an "Etag", and subsequent requests of the
- same client should provide the tag in an "If-None-Match" header
- to detect if the privacy policy has changed. If not, a
- "204 Not Modified" response will be returned.
-
- If the "Etag" is missing, the client should not cache the response and instead prompt the user again at the next opportunity. This is usually only the case if the privacy policy was not configured correctly.
+.. _keys:
---------------------------
Exchange status information
@@ -94,6 +56,75 @@ possibly by using HTTPS.
returned MUST be mixed with locally generated entropy.
+.. 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.
+ This specification corresponds to ``current`` protocol being **v19**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body is a `VersionResponse`.
+
+ .. ts:def:: ExchangeVersionResponse
+
+ interface ExchangeVersionResponse {
+ // libtool-style representation of the Exchange 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-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
Get a list of all denomination keys offered by the exchange,
@@ -120,9 +151,55 @@ possibly by using HTTPS.
// The format is "current:revision:age".
version: string;
- // The exchange's currency.
+ // The exchange's base URL.
+ base_url: string;
+
+ // 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;
@@ -131,14 +208,26 @@ possibly by using HTTPS.
// not signed (!), can change without notice.
reserve_closing_delay: RelativeTime;
- // Maximum amount that a wallet is allowed to hold without
- // having to undergo the KYC process of the issuing
+ // Threshold amounts beyond which wallet should
+ // trigger the KYC process of the issuing
// exchange. Optional option, if not given there is no limit.
// Currency must match ``currency``.
- wallet_balance_limit_without_kyc?: Amount;
+ wallet_balance_limit_without_kyc?: Amount[];
+
+ // Denominations offered by this exchange
+ denominations: DenomGroup[];
- // Denominations offered by this exchange.
- denoms: Denom[];
+ // 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[];
@@ -155,23 +244,99 @@ possibly by using HTTPS.
// The exchange's signing keys.
signkeys: SignKey[];
- // 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`
- eddsa_sig: EddsaSignature;
+ // Optional field with a dictionary of (name, object) pairs defining the
+ // supported and enabled extensions, such as ``age_restriction``.
+ extensions?: { name: ExtensionManifest };
+
+ // Signature by the exchange master key of the SHA-256 hash of the
+ // normalized JSON-object of field extensions, if it was set.
+ // The signature has purpose TALER_SIGNATURE_MASTER_EXTENSIONS.
+ extensions_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
@@ -184,11 +349,6 @@ possibly by using HTTPS.
// What date (exclusive) does this fees stop going into effect?
end_date: Timestamp;
- // KYC fee, charged when a user wants to create an account.
- // The first year of the account_annual_fee after the KYC is
- // always included.
- kyc_fee: Amount;
-
// Account history fee, charged when a user wants to
// obtain a reserve/account history.
history_fee: Amount;
@@ -210,13 +370,6 @@ possibly by using HTTPS.
// retain the account history for legal reasons until this time.
history_expiration: RelativeTime;
- // How long does the exchange promise to keep funds
- // an account for which the KYC has never happened
- // after a purse was merged into an account? Basically,
- // after this time funds in an account without KYC are
- // forfeit.
- account_kyc_timeout: RelativeTime;
-
// Non-negative number of concurrent purses that any
// account holder is allowed to create without having
// to pay the purse_fee.
@@ -233,26 +386,78 @@ possibly by using HTTPS.
}
- .. ts:def:: Denom
+ .. ts:def:: AgeMask
+
+ // Binary representation of the age groups.
+ // The bits set in the mask mark the edges at the beginning of a next age
+ // group. F.e. for the age groups
+ // 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-21, 21-*
+ // the following bits are set:
+ //
+ // 31 24 16 8 0
+ // | | | | |
+ // oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
+ //
+ // A value of 0 means that the exchange does not support the extension for
+ // age-restriction.
+ type AgeMask = Integer;
+
+ .. ts:def:: DenomGroup
+
+ type DenomGroup =
+ | DenomGroupRsa
+ | DenomGroupCs
+ | DenomGroupRsaAgeRestricted
+ | DenomGroupCsAgeRestricted;
+
+ .. ts:def:: DenomGroupRsa
+
+ interface DenomGroupRsa extends DenomGroupCommon {
+ cipher: "RSA";
- interface Denom {
- // How much are coins of this denomination worth?
- value: Amount;
+ denoms: ({
+ rsa_pub: RsaPublicKey;
+ } & DenomCommon)[];
+ }
- // When does the denomination key become valid?
- stamp_start: Timestamp;
+ .. ts:def:: DenomGroupCs
- // When is it no longer possible to deposit coins
- // of this denomination?
- stamp_expire_withdraw: Timestamp;
+ interface DenomGroupCs extends DenomGroupCommon {
+ cipher: "CS";
- // 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;
+ denoms: ({
+ cs_pub: Cs25519Point;
+ } & DenomCommon)[];
+ }
- // Public key for the denomination.
- denom_pub: DenominationKey;
+ .. ts:def:: DenomGroupRsaAgeRestricted
+
+ interface DenomGroupRsaAgeRestricted extends DenomGroupCommon {
+ cipher: "RSA+age_restricted";
+ age_mask: AgeMask;
+
+ denoms: ({
+ rsa_pub: RsaPublicKey;
+ } & DenomCommon)[];
+ }
+
+ .. ts:def:: DenomGroupCsAgeRestricted
+
+ interface DenomGroupCSAgeRestricted extends DenomGroupCommon {
+ cipher: "CS+age_restricted";
+ age_mask: AgeMask;
+
+ denoms: ({
+ cs_pub: Cs25519Point;
+ } & DenomCommon)[];
+ }
+
+ .. ts:def:: DenomGroupCommon
+
+ // Common attributes for all denomination groups
+ interface DenomGroupCommon {
+ // How much are coins of this denomination worth?
+ value: Amount;
// Fee charged by the exchange for withdrawing a coin of this denomination.
fee_withdraw: Amount;
@@ -266,8 +471,38 @@ possibly by using HTTPS.
// Fee charged by the exchange for refunding a coin of this denomination.
fee_refund: Amount;
+ }
+
+ .. ts:def:: DenomCommon
+
+ interface DenomCommon {
// Signature of `TALER_DenominationKeyValidityPS`.
master_sig: EddsaSignature;
+
+ // When does the denomination key become valid?
+ stamp_start: Timestamp;
+
+ // When is it no longer possible to withdraw coins
+ // of this denomination?
+ stamp_expire_withdraw: Timestamp;
+
+ // When is it no longer possible to deposit coins
+ // of this denomination?
+ 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;
+
+ // 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
@@ -391,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
@@ -452,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.
@@ -479,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;
@@ -563,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;
@@ -645,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`.
@@ -820,7 +1008,7 @@ Management operations authorized by master key
.. http:post:: /management/wire-fee
- This request will be used to configure wire fees.
+ This request is used to configure wire fees.
**Request:**
@@ -860,9 +1048,6 @@ Management operations authorized by master key
// Wire fee to charge during that time period for this wire method.
wire_fee: Amount;
- // Wad fee to charge during that time period for this wire method.
- wad_fee: Amount;
-
}
.. http:post:: /management/global-fees
@@ -928,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
@@ -978,444 +1173,474 @@ Management operations authorized by master key
}
+.. http:post:: /management/drain
+
+ This request is used to drain profits from the
+ exchange's escrow account to another regular
+ bank account of the exchange. The actual drain
+ requires running the ``taler-exchange-drain`` tool.
-.. http:post:: /management/partners
+ **Request:**
- Enables a partner exchange for wad transfers.
+ The request must be a `DrainProfitsMessage`.
- .. note::
+ **Response:**
- This is a draft API that is not yet implemented.
+ :http:statuscode:`204 No content`:
+ The profit drain was scheduled.
+ :http:statuscode:`403 Forbidden`:
+ The master signature is invalid.
+
+ **Details:**
+
+ .. ts:def:: DrainProfitsMessage
+
+ interface DrainProfitsMessage {
+
+ // Configuration section of the account to debit.
+ debit_account_section: string;
+
+ // Credit payto URI
+ credit_payto_uri: string;
+
+ // Wire transfer identifier to use.
+ wtid: Base32;
+
+ // Signature by the exchange master key over a
+ // `TALER_MasterDrainProfitPS`.
+ // Must have purpose ``TALER_SIGNATURE_MASTER_DRAIN_PROFITS``.
+ master_sig: EddsaSignature;
+
+ // When was the message created.
+ date: Timestamp;
+
+ // Amount to be drained.
+ amount: Amount;
+
+ }
+
+
+.. http:post:: /management/aml-officers
+
+ Update settings for an AML Officer status.
**Request:**
- The request must be an `ExchangePartner` message.
+ The request must be an `AmlOfficerSetup` message.
**Response**
:http:statuscode:`204 No content`:
- The partner has been added successfully.
+ The officer settings have been updated successfully.
:http:statuscode:`403 Forbidden`:
The signature is invalid.
:http:statuscode:`409 Conflict`:
The exchange has previously received a conflicting configuration message.
+ **Details:**
+ .. ts:def:: AmlOfficerSetup
+ interface AmlOfficerSetup {
----------------
-Auditor actions
----------------
+ // Public key of the AML officer
+ officer_pub: EddsaPublicKey;
-.. _auditor_action:
+ // Legal full name of the AML officer
+ officer_name: string;
-This part of the API is for the use by auditors interacting with the exchange.
+ // Is the account active?
+ is_active: boolean;
+ // Is the account read-only?
+ read_only: boolean;
-.. http:post:: /auditors/$AUDITOR_PUB/$H_DENOM_PUB
+ // Signature by the exchange master key over a
+ // `TALER_MasterAmlOfficerStatusPS`.
+ // Must have purpose ``TALER_SIGNATURE_MASTER_AML_KEY``.
+ master_sig: EddsaSignature;
- This is used to add an auditor signature to the ``/keys`` response. It
- affirms to wallets and merchants that this auditor is indeed auditing
- the coins issued by the respective denomination. There is no "delete"
- operation for this, as auditors can only stop auditing a denomination
- when it expires.
+ // When will the change take effect?
+ change_date: Timestamp;
+
+ }
+
+
+ .. http:post:: /management/partners
+
+ Enables a partner exchange for wad transfers.
**Request:**
- The request must be a `AuditorSignatureAddMessage`.
+ The request must be an `ExchangePartner` message.
- **Response:**
+ **Response**
:http:statuscode:`204 No content`:
- The backend has successfully stored the auditor signature.
+ The partner has been added successfully.
:http:statuscode:`403 Forbidden`:
- The auditor signature is invalid.
- :http:statuscode:`404 Not found`:
- The denomination key for which the auditor is providing a signature is unknown.
- The response will be a `DenominationUnknownMessage`.
- :http:statuscode:`410 Gone`:
- This auditor is no longer supported by the exchange.
- :http:statuscode:`412 Precondition failed`:
- This auditor is not yet known to the exchange.
+ The signature is invalid.
+ :http:statuscode:`409 Conflict`:
+ The exchange has previously received a conflicting configuration message.
**Details:**
- .. ts:def:: DenominationUnknownMessage
+ .. ts:def:: ExchangePartner
- interface DenominationUnknownMessage {
+ interface ExchangePartner {
- // Taler error code.
- code: number;
+ // Base URL of the partner exchange
+ partner_base_url: string;
- // Signature by the exchange over a
- // `TALER_DenominationUnknownAffirmationPS`.
- // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN``.
- exchange_sig: EddsaSignature;
+ // Master (offline) public key of the partner exchange.
+ partner_pub: EddsaPublicKey;
- // Public key of the exchange used to create
- // the 'exchange_sig.
- exchange_pub: EddsaPublicKey;
+ // How frequently will wad transfers be made
+ wad_frequency: RelativeTime;
- // Hash of the denomination public key that is unknown.
- h_denom_pub: HashCode;
+ // Signature by the exchange master key over a
+ // `TALER_PartnerConfigurationPS`.
+ // Must have purpose ``TALER_SIGNATURE_MASTER_PARTNER_DETAILS``.
+ master_sig: EddsaSignature;
- // When was the signature created.
- timestamp: Timestamp;
+ // When will the partner relationship start (inclusive).
+ start_date: Timestamp;
+
+ // When will the partner relationship end (exclusive).
+ end_date: Timestamp;
+
+ // Wad fee to be charged (to customers).
+ wad_fee: Amount;
}
- .. ts:def:: AuditorSignatureAddMessage
+--------------
+AML operations
+--------------
- interface AuditorSignatureAddMessage {
+This API is only for designated AML officers. It is used
+to allow exchange staff to monitor suspicious transactions
+and freeze or unfreeze accounts suspected of money laundering.
- // Signature by the auditor over a
- // `TALER_ExchangeKeyValidityPS`.
- // Must have purpose ``TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS``.
- auditor_sig: EddsaSignature;
- }
+.. http:get:: /aml/$OFFICER_PUB/decisions/$STATE
+ Obtain list of AML decisions (filtered by $STATE). ``$STATE`` must be either ``normal``, ``pending`` or ``frozen``.
-----------
-Withdrawal
-----------
+ *Taler-AML-Officer-Signature*: The client must provide Base-32 encoded EdDSA signature with ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that this is merely a simple authentication mechanism, the details of the request are not protected by the signature.
-This API is used by the wallet to obtain digital coins.
+ :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 (before ``start``).
+ :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation. Defaults to ``INT64_MAX``, namely the biggest row id possible in the database.
-When transferring money to the exchange such as via SEPA transfers, the exchange creates
-a *reserve*, which keeps the money from the customer. The customer must
-specify an EdDSA reserve public key as part of the transfer, and can then
-withdraw digital coins using the corresponding private key. All incoming and
-outgoing transactions are recorded under the corresponding public key by the
-exchange.
+ **Response**
-.. note::
+ :http:statuscode:`200 OK`:
+ The responds will be an `AmlRecords` message.
+ :http:statuscode:`204 No content`:
+ There are no matching AML records.
+ :http:statuscode:`403 Forbidden`:
+ The signature is invalid.
+ :http:statuscode:`404 Not found`:
+ The designated AML account is not known.
+ :http:statuscode:`409 Conflict`:
+ The designated AML account is not enabled.
- Eventually the exchange will need to advertise a policy for how long it will
- keep transaction histories for inactive or even fully drained reserves. We
- will therefore need some additional handler similar to ``/keys`` to
- advertise those terms of service.
+ **Details:**
+ .. ts:def:: AmlRecords
-.. http:get:: /reserves/$RESERVE_PUB
+ interface AmlRecords {
- Request information about a reserve.
+ // Array of AML records matching the query.
+ records: AmlRecord[];
+ }
- **Request:**
+ .. ts:def:: AmlRecord
- :query timeout_ms=MILLISECONDS: *Optional.* If specified, the exchange will wait up to MILLISECONDS for incoming funds before returning a 404 if the reserve does not yet exist.
+ interface AmlRecord {
- **Response:**
+ // Which payto-address is this record about.
+ // Identifies a GNU Taler wallet or an affected bank account.
+ h_payto: PaytoHash;
- :http:statuscode:`200 OK`:
- The exchange responds with a `ReserveSummary` object; the reserve was known to the exchange.
- :http:statuscode:`404 Not found`:
- The reserve key does not belong to a reserve known to the exchange.
+ // What is the current AML state.
+ current_state: Integer;
- **Details:**
+ // Monthly transaction threshold before a review will be triggered
+ threshold: Amount;
- .. ts:def:: ReserveSummary
+ // RowID of the record.
+ rowid: Integer;
- interface ReserveSummary {
- // Balance left in the reserve.
- balance: Amount;
}
-
-.. http:post:: /reserves/$RESERVE_PUB/status
- Request information about a reserve or an account.
+.. http:get:: /aml/$OFFICER_PUB/decision/$H_PAYTO
- **Request:**
+ Obtain deails about an AML decision.
- The request body must be a `ReserveStatusRequest` object.
+ *Taler-AML-Officer-Signature*: The client must provide Base-32 encoded EdDSA signature with ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that this is merely a simple authentication mechanism, the details of the request are not protected by the signature.
- **Response:**
+ :query history: *Optional*. If set to yes, we return all historic decisions and not only the last one.
+
+ **Response**
:http:statuscode:`200 OK`:
- The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange.
- :http:statuscode:`401 Unauthorized`:
- The *TALER_SIGNATURE_RESERVE_STATUS_REQUEST* signature is invalid.
- This response comes with a standard `ErrorDetail` response.
+ The responds will be an `AmlDecisionDetails` message.
+ :http:statuscode:`204 No content`:
+ There are no matching AML records for the given payto://-URI.
:http:statuscode:`403 Forbidden`:
- The provided timestamp is not close to the current time.
+ The signature is invalid.
:http:statuscode:`404 Not found`:
- The reserve key does not belong to a reserve known to the exchange.
+ The designated AML account is not known.
+ :http:statuscode:`409 Conflict`:
+ The designated AML account is not enabled.
**Details:**
- .. ts:def:: ReserveStatusRequest
+ .. ts:def:: AmlDecisionDetails
- interface ReserveStatusRequest {
- // Signature of purpose
- // ``TALER_SIGNATURE_RESERVE_STATUS_REQUEST`` over
- // a `TALER_ReserveStatusRequestSignaturePS`.
- reserve_sig: EddsaSignature;
+ interface AmlDecisionDetails {
- // 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;
- }
+ // Array of AML decisions made for this account. Possibly
+ // contains only the most recent decision if "history" was
+ // not set to 'true'.
+ aml_history: AmlDecisionDetail[];
- .. ts:def:: ReserveStatus
-
- interface ReserveStatus {
- // Balance left in the reserve.
- balance: Amount;
-
- // True if the owner of the account currently satisfies
- // the required KYC checks.
- kyc_passed: boolean;
-
- // True if the reserve history includes a merge of a purse
- // and thus the owner must pass KYC checks before withdrawing.
- kyc_required: boolean;
-
- // Transaction history for this reserve.
- // May be partial (!).
- history: TransactionHistoryItem[];
+ // Array of KYC attributes obtained for this account.
+ kyc_attributes: KycDetail[];
}
- Objects in the transaction history have the following format:
+ .. ts:def:: AmlDecisionDetail
- .. ts:def:: TransactionHistoryItem
-
- // Union discriminated by the "type" field.
- type TransactionHistoryItem =
- | AccountMergeTransaction
- | AccountSetupTransaction
- | ReserveHistoryTransaction
- | ReserveWithdrawTransaction
- | ReserveCreditTransaction
- | ReserveClosingTransaction
- | ReserveRecoupTransaction;
+ interface AmlDecisionDetail {
- .. ts:def:: ReserveHistoryTransaction
+ // What was the justification given?
+ justification: string;
- interface ReserveHistoryTransaction {
- type: "HISTORY";
+ // What is the new AML state.
+ new_state: Integer;
- // Fee agreed to by the reserve owner.
- history_fee: Amount;
+ // When was this decision made?
+ decision_time: Timestamp;
- // Time when the request was made.
- request_timestamp: Timestamp;
+ // What is the new AML decision threshold (in monthly transaction volume)?
+ new_threshold: Amount;
- // Signature created with the reserve's private key.
- // Must be of purpose ``TALER_SIGNATURE_RESERVE_HISTORY_REQUEST`` over
- // a `TALER_ReserveHistoryRequestSignaturePS`.
- reserve_sig: EddsaSignature;
+ // Who made the decision?
+ decider_pub: AmlOfficerPublicKeyP;
}
- .. ts:def:: AccountSetupTransaction
+ .. ts:def:: KycDetail
- interface AccountSetupTransaction {
- type: "SETUP";
+ interface KycDetail {
- // KYC fee agreed to by the reserve owner.
- kyc_fee: Amount;
+ // Name of the configuration section that specifies the provider
+ // which was used to collect the KYC details
+ provider_section: string;
- // Time when the KYC was triggered.
- kyc_timestamp: Timestamp;
+ // The collected KYC data. NULL if the attribute data could not
+ // be decrypted (internal error of the exchange, likely the
+ // attribute key was changed).
+ attributes?: Object;
- // 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;
+ // Time when the KYC data was collected
+ collection_time: Timestamp;
- // Signature created with the reserve's private key.
- // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_SETUP_REQUEST`` over
- // a ``TALER_AccountSetupRequestSignaturePS``.
- reserve_sig: EddsaSignature;
+ // Time when the validity of the KYC data will expire
+ expiration_time: Timestamp;
}
- .. ts:def:: AccountMergeTransaction
- interface AccountMergeTransaction {
- type: "MERGE";
+ .. http:post:: /aml/$OFFICER_PUB/decision
- // Actual amount merged (what was left after fees).
- amount: Amount;
-
- // Minimum amount merged (amount signed by the
- // reserve and purse signatures).
- minimum_amount: Amount;
+ Make an AML decision. Triggers the respective action and
+ records the justification.
- // Purse that was merged.
- purse_pub: EddsaPublicKey;
+ **Request:**
- // Time of the merge.
- merge_timestamp: Timestamp;
+ The request must be an `AmlDecision` message.
- // Expiration time of the purse.
- purse_expiration: Timestamp;
+ **Response**
- // Hash of the contract.
- h_contract: HashCode;
+ :http:statuscode:`204 No content`:
+ The AML decision has been executed and recorded successfully.
+ :http:statuscode:`403 Forbidden`:
+ The signature is invalid.
+ :http:statuscode:`404 Not found`:
+ The address the decision was made upon is unknown to the exchange or
+ the designated AML account is not known.
+ :http:statuscode:`409 Conflict`:
+ The designated AML account is not enabled or a more recent
+ decision was already submitted.
- // Hash of the wire details of the reserve.
- h_wire: HashCode;
+ **Details:**
- // Signature created with the reserve's private key.
- // Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE`` over
- // a `TALER_AccountMergeSignaturePS`.
- reserve_sig: EddsaSignature;
+ .. ts:def:: AmlDecision
- // Signature created with the purse's private key.
- // Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE``
- // over a `TALER_PurseMergeSignaturePS`.
- purse_sig: EddsaSignature;
+ interface AmlDecision {
- // Deposit fees that were charged to the purse.
- deposit_fees: Amount;
- }
+ // Human-readable justification for the decision.
+ justification: string;
- .. ts:def:: ReserveWithdrawTransaction
+ // At what monthly transaction volume should the
+ // decision be automatically reviewed?
+ new_threshold: Amount;
- interface ReserveWithdrawTransaction {
- type: "WITHDRAW";
+ // Which payto-address is the decision about?
+ // Identifies a GNU Taler wallet or an affected bank account.
+ h_payto: PaytoHash;
- // Amount withdrawn.
- amount: Amount;
+ // What is the new AML state (e.g. frozen, unfrozen, etc.)
+ // Numerical values are defined in `AmlDecisionState`.
+ new_state: Integer;
- // Hash of the denomination public key of the coin.
- h_denom_pub: HashCode;
+ // Signature by the AML officer over a `TALER_AmlDecisionPS`.
+ // Must have purpose ``TALER_SIGNATURE_MASTER_AML_KEY``.
+ officer_sig: EddsaSignature;
- // Hash of the blinded coin to be signed.
- h_coin_envelope: HashCode;
+ // When was the decision made?
+ decision_time: Timestamp;
- // Signature over a `TALER_WithdrawRequestPS`
- // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW``
- // created with the reserve's private key.
- reserve_sig: EddsaSignature;
+ // Optional argument to impose new KYC requirements
+ // that the customer has to satisfy to unblock transactions.
+ kyc_requirements?: string[];
+ }
- // Fee that is charged for withdraw.
- withdraw_fee: Amount;
- }
+---------------
+Auditor actions
+---------------
- .. ts:def:: ReserveCreditTransaction
+.. _auditor_action:
- interface ReserveCreditTransaction {
- type: "CREDIT";
+This part of the API is for the use by auditors interacting with the exchange.
- // Amount deposited.
- amount: Amount;
- // Sender account ``payto://`` URL.
- sender_account_url: string;
+.. http:post:: /auditors/$AUDITOR_PUB/$H_DENOM_PUB
- // Opaque identifier internal to the exchange that
- // uniquely identifies the wire transfer that credited the reserve.
- wire_reference: Integer;
+ This is used to add an auditor signature to the ``/keys`` response. It
+ affirms to wallets and merchants that this auditor is indeed auditing
+ the coins issued by the respective denomination. There is no "delete"
+ operation for this, as auditors can only stop auditing a denomination
+ when it expires.
- // Timestamp of the incoming wire transfer.
- timestamp: Timestamp;
- }
+ **Request:**
+ The request must be a `AuditorSignatureAddMessage`.
- .. ts:def:: ReserveClosingTransaction
+ **Response:**
- interface ReserveClosingTransaction {
- type: "CLOSING";
+ :http:statuscode:`204 No content`:
+ The backend has successfully stored the auditor signature.
+ :http:statuscode:`403 Forbidden`:
+ The auditor signature is invalid.
+ :http:statuscode:`404 Not found`:
+ The denomination key for which the auditor is providing a signature is unknown.
+ The response will be a `DenominationUnknownMessage`.
+ :http:statuscode:`410 Gone`:
+ This auditor is no longer supported by the exchange.
+ :http:statuscode:`412 Precondition failed`:
+ This auditor is not yet known to the exchange.
- // Closing balance.
- amount: Amount;
+ **Details:**
- // Closing fee charged by the exchange.
- closing_fee: Amount;
+ .. ts:def:: DenominationUnknownMessage
- // Wire transfer subject.
- wtid: string;
+ interface DenominationUnknownMessage {
- // ``payto://`` URI of the wire account into which the funds were returned to.
- receiver_account_details: string;
+ // Taler error code.
+ code: number;
- // This is a signature over a
- // struct `TALER_ReserveCloseConfirmationPS` with purpose
- // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``.
+ // Signature by the exchange over a
+ // `TALER_DenominationUnknownAffirmationPS`.
+ // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN``.
exchange_sig: EddsaSignature;
- // Public key used to create 'exchange_sig'.
+ // Public key of the exchange used to create
+ // the 'exchange_sig.
exchange_pub: EddsaPublicKey;
- // Time when the reserve was closed.
+ // Hash of the denomination public key that is unknown.
+ h_denom_pub: HashCode;
+
+ // When was the signature created.
timestamp: Timestamp;
+
}
+ .. ts:def:: AuditorSignatureAddMessage
- .. ts:def:: ReserveRecoupTransaction
+ interface AuditorSignatureAddMessage {
- interface ReserveRecoupTransaction {
- type: "RECOUP";
+ // Signature by the auditor over a
+ // `TALER_ExchangeKeyValidityPS`.
+ // Must have purpose ``TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS``.
+ auditor_sig: EddsaSignature;
- // Amount paid back.
- amount: Amount;
+ }
- // This is a signature over
- // a struct `TALER_RecoupConfirmationPS` with purpose
- // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP``.
- exchange_sig: EddsaSignature;
+.. _exchange-withdrawal:
- // Public key used to create 'exchange_sig'.
- exchange_pub: EddsaPublicKey;
+----------
+Withdrawal
+----------
- // Time when the funds were paid back into the reserve.
- timestamp: Timestamp;
+This API is used by the wallet to obtain digital coins.
- // Public key of the coin that was paid back.
- coin_pub: CoinPublicKey;
- }
+When transferring money to the exchange such as via SEPA transfers, the exchange creates
+a *reserve*, which keeps the money from the customer. The customer must
+specify an EdDSA reserve public key as part of the transfer, and can then
+withdraw digital coins using the corresponding private key. All incoming and
+outgoing transactions are recorded under the corresponding public key by the
+exchange.
+
+.. note::
+
+ Eventually the exchange will need to advertise a policy for how long it will
+ keep transaction histories for inactive or even fully drained reserves. We
+ will therefore need some additional handler similar to ``/keys`` to
+ advertise those terms of service.
-.. http:post:: /reserves/$RESERVE_PUB/history
+.. http:get:: /reserves/$RESERVE_PUB
- Request information about the full history of
- a reserve or an account.
+ Request summary information about a reserve.
**Request:**
- The request body must be a `ReserveHistoryRequest` object.
+ :query timeout_ms=MILLISECONDS: *Optional.* If specified, the exchange will wait up to MILLISECONDS for incoming funds before returning a 404 if the reserve does not yet exist.
**Response:**
:http:statuscode:`200 OK`:
- The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange.
- :http:statuscode:`401 Unauthorized`:
- The *TALER_SIGNATURE_RESERVE_HISTORY_REQUEST* is invalid.
- This response comes with a standard `ErrorDetail` response.
- :http:statuscode:`403 Forbidden`:
- The provided timestamp is not close to the current time.
+ The exchange responds with a `ReserveSummary` object; the reserve was known to the exchange.
: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
+ .. ts:def:: ReserveSummary
- interface ReserveHistoryRequest {
- // Signature of type
- // ``TALER_SIGNATURE_RESERVE_HISTORY_REQUEST``
- // over a `TALER_ReserveHistoryRequestSignaturePS`.
- reserve_sig: EddsaSignature;
+ interface ReserveSummary {
+ // Balance left in the reserve.
+ balance: Amount;
- // 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;
+ // If set, age restriction is required to be set for each coin to this
+ // value during the withdrawal from this reserve. The client then MUST
+ // use a denomination with support for age restriction enabled for the
+ // withdrawal.
+ // The value represents a valid age group from the list of permissible
+ // age groups as defined by the exchange's output to /keys.
+ maximum_age_group?: number;
}
+Withdraw
+~~~~~~~~
+
.. http:post:: /csr-withdraw
Obtain exchange-side input values in preparation for a
@@ -1452,13 +1677,13 @@ exchange.
// Hash of the public key of the denomination the
// request relates to.
- denom_pub_hash: Hash;
+ denom_pub_hash: HashCode;
}
.. ts:def:: WithdrawPrepareResponse
- type WithdrawPrepareResponse
+ type WithdrawPrepareResponse =
| ExchangeWithdrawValue;
.. ts:def:: ExchangeWithdrawValue
@@ -1485,24 +1710,55 @@ exchange.
r_pub_1: CsRPublic;
}
-.. http:post:: /reserves/$RESERVE_PUB/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
+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 `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.
- :http:statuscode:`202 Accepted`:
+ 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`:
+ 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`:
+ 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
@@ -1521,65 +1777,21 @@ exchange.
a different payto://-URI for the KYC check depending
on the case.
- :http:statuscode:`403 Forbidden`:
- The signature is invalid.
- :http:statuscode:`404 Not found`:
- The 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 a coin of the indicated denomination.
- The response is `WithdrawError` object.
- :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.
**Details:**
- .. ts:def:: DenominationExpiredMessage
-
- interface DenominationExpiredMessage {
+ .. ts:def:: BatchWithdrawRequest
- // 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;
+ interface BatchWithdrawRequest {
+ // Array of requests for the individual coins to withdraw.
+ planchets: WithdrawRequest[];
- // 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;
-
- // When was the signature created.
- timestamp: Timestamp;
-
- // 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;
@@ -1594,6 +1806,16 @@ exchange.
}
+
+ .. 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 {
@@ -1636,10 +1858,23 @@ exchange.
.. ts:def:: KycNeededRedirect
interface KycNeededRedirect {
- // Payment target that the merchant should
+
+ // Numeric `error code <error-codes>` unique to the condition.
+ // Should always be ``TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED``.
+ code: number;
+
+ // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ...
+ // Should give a human-readable hint about the error's nature. Optional, may change without notice!
+ hint?: string;
+
+ // Hash of the payto:// account URI that identifies
+ // the account which is being KYCed.
+ h_payto: PaytoHash;
+
+ // Legitimization target that the merchant should
// use to check for its KYC status using
- // the ``/kyc-check/$PAYMENT_TARGET_UUID`` endpoint.
- payment_target_uuid: Integer;
+ // the ``/kyc-check/$REQUIREMENT_ROW/...`` endpoint.
+ requirement_row: Integer;
}
@@ -1656,243 +1891,646 @@ exchange.
balance: Amount;
// History of the reserve's activity, in the same format
- // as returned by ``/reserve/status``.
+ // as returned by ``/reserve/$RID/history``.
history: TransactionHistoryItem[]
}
-.. _delete-reserve:
+ .. ts:def:: DenominationExpiredMessage
-.. http:DELETE:: /reserves/$RESERVE_PUB
+ interface DenominationExpiredMessage {
- Forcefully closes a reserve.
- The request header must contain an *Account-Request-Signature*.
+ // 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;
- **Request:**
+ // Signature by the exchange over a
+ // `TALER_DenominationExpiredAffirmationPS`.
+ // Must have purpose ``TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED``.
+ exchange_sig: EddsaSignature;
- *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``.
+ // Public key of the exchange used to create
+ // the 'exchange_sig.
+ exchange_pub: EddsaPublicKey;
- :query force=BOOLEAN: *Optional.* If set to 'true' specified, the exchange
- will delete the account even if there is a balance remaining.
+ // Hash of the denomination public key that is unknown.
+ h_denom_pub: HashCode;
+
+ // When was the signature created.
+ timestamp: Timestamp;
+
+ // What kind of operation was requested that now
+ // failed?
+ oper: string;
+ }
+
+
+
+
+
+Withdraw with Age Restriction
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the reserve was marked with a maximum age group, the client has to perform a
+cut&choose protocol with the exchange. It first calls
+``/reserves/$RESERVE_PUB/age-withdraw`` and commits to ``n*kappa`` coins. On
+success, the exchange answers this request with an noreveal-index. The client
+then has to call ``/age-withdraw/$ACH/reveal`` to reveal all ``n*(kappa - 1)``
+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
+
+ Withdraw multiple coins *with age restriction* 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 `AgeWithdrawRequest` object.
**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:`401 Unauthorized`:
- The *Account-Request-Signature* is invalid.
+ The request was successful, and the response is a `AgeWithdrawResponse`.
+ 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`:
- 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.
+ One of two reasons occured:
- **Details:**
+ 1. The balance of the reserve is not sufficient to withdraw the coins of the
+ given amount. The response is a `WithdrawError` object.
- .. ts:def:: ReserveClosedResponse
-
- interface ReserveClosedResponse {
+ 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.
+ 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.
- // Final balance of the account.
- closing_amount: Amount;
+ .. ts:def:: AgeWithdrawRequest
+
+ interface AgeWithdrawRequest {
+ // 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.
+ max_age: number;
+
+ // Signature of `TALER_AgeWithdrawRequestPS` created with
+ // the `reserves's private key <reserve-priv>`
+ // using purpose ``TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW``.
+ reserve_sig: EddsaSignature;
+ }
- // Current time of the exchange, used as part of
- // what the exchange signs over.
- close_time: Timestamp;
+ .. ts:def:: AgeWithdrawResponse
- // 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;
+ interface AgeWithdrawResponse {
+ // index of the commitments that the client doesn't
+ // have to disclose
+ noreveal_index: Integer;
- // This is a signature over a
- // struct ``TALER_AccountDeleteConfirmationPS`` with purpose
- // ``TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED``.
+ // Signature of `TALER_AgeWithdrawConfirmationPS` whereby
+ // the exchange confirms the ``noreveal_index``.
exchange_sig: EddsaSignature;
+ // `Public EdDSA key <sign-key-pub>` 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: EddsaPublicKey;
}
-.. _deposit-par:
+.. http:post:: /age-withdraw/$ACH/reveal
--------
-Deposit
--------
+ The client has previously committed to multiple coins with age restriction
+ in a call to ``/reserve/$RESERVE_PUB/age-withdraw`` and got a
+ `AgeWithdrawResponse` from the exchange. By calling this
+ endpoint, the client has to reveal each coin and their ``kappa - 1``
+ age commitments, except for the age commitments with index
+ ``noreveal_index``. The hash of all commitments from the former withdraw
+ request is given as the ``$ACH`` value in the URL to this endpoint.
-Deposit operations are requested by a merchant during a transaction. For the
-deposit operation, 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.
-.. _deposit:
+ **Request:** The request body must be a `AgeWithdrawRevealRequest` object.
-.. http:POST:: /coins/$COIN_PUB/deposit
+ **Response:**
- 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.
+ :http:statuscode:`200 OK`:
+ 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.
+ :http:statuscode:`404 Not found`:
+ The provided commitment $ACH is unknown.
+ :http:statuscode:`409 Conflict`:
+ The reveal operation failed and the response is an `WithdrawError` object.
+ The error codes indicate one of two cases:
- 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.
+ 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 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 {
+ // 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.
+ //
+ // 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 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
+
+ // The value for ``PublishedAgeRestrictionBaseKey`` is a randomly chosen
+ // `Edx25519PublicKey` for which the private key is not known to the clients. It is
+ // used during the age-withdraw protocol so that clients can proof that they
+ // derived all public keys to age groups higher than their allowed maximum
+ // from this particular value.
+ const PublishedAgeRestrictionBaseKey =
+ new Edx25519PublicKey("CH0VKFDZ2GWRWHQBBGEK9MWV5YDQVJ0RXEE0KYT3NMB69F0R96TG");
+
+ .. ts:def:: AgeWithdrawRevealResponse
+
+ interface AgeWithdrawRevealResponse {
+ // List of the exchange's blinded RSA signatures on the new coins.
+ ev_sigs : BlindedDenominationSignature[];
+ }
+
+
+.. _reserve-history:
+
+---------------
+Reserve History
+---------------
+
+.. http:get:: /reserves/$RESERVE_PUB/history
+
+ Request information about the full history of
+ a reserve or an account.
**Request:**
- The request body must be a `DepositRequest` object.
+ 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 reserve history matches the provided Etag.
+
+ *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.
+
+ :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.
- :http:statuscode:`401 Unauthorized`:
- One of the signatures is invalid.
+ 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`:
+ 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_DEPOSIT_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.
- 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
- // Exchange's unblinded RSA signature of the coin.
- ub_sig: DenominationSignature;
+ // Union discriminated by the "type" field.
+ type TransactionHistoryItem =
+ | AccountSetupTransaction
+ | ReserveWithdrawTransaction
+ | ReserveAgeWithdrawTransaction
+ | ReserveCreditTransaction
+ | ReserveClosingTransaction
+ | ReserveOpenRequestTransaction
+ | ReserveCloseRequestTransaction
+ | PurseMergeTransaction;
- // Timestamp when the contract was finalized.
- timestamp: Timestamp;
+ .. ts:def:: AccountSetupTransaction
- // 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;
+ interface AccountSetupTransaction {
+ type: "SETUP";
- // EdDSA `public key of the merchant <merchant-pub>`, so that the client can identify the
- // merchant for refund requests.
- merchant_pub: EddsaPublicKey;
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- // Date until which the merchant can issue a refund to the customer via the
- // exchange, to be omitted if refunds are not allowed.
- refund_deadline?: Timestamp;
+ // 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;
- // 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;
+ }
+
+ .. ts:def:: ReserveAgeWithdrawTransaction
+
+ interface ReserveAgeWithdrawTransaction {
+ type: "AGEWITHDRAW";
+
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
+ // 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;
+ }
- // s value component of the signature.
- cs_signature_s: Cs25519Scalar:
+ .. 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;
}
- 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:: ReserveClosingTransaction
- .. ts:def:: DepositSuccess
+ interface ReserveClosingTransaction {
+ type: "CLOSING";
- 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;
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
- // Timestamp when the deposit was received by the exchange.
- exchange_timestamp: Timestamp;
+ // Closing balance.
+ amount: Amount;
- // 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.
+ // 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 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.
+ // Public key used to create 'exchange_sig'.
exchange_pub: EddsaPublicKey;
+
+ // Time when the reserve was closed.
+ timestamp: Timestamp;
}
- .. ts:def:: DepositDoubleSpendError
- interface DepositDoubleSpendError {
- // The string constant "insufficient funds".
- hint: string;
+ .. ts:def:: ReserveOpenRequestTransaction
- // Transaction history for the coin that is
- // being double-spended.
+ interface ReserveOpenRequestTransaction {
+ type: "OPEN";
+
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
+ // 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";
+
+ // Offset of this entry in the reserve history.
+ // Useful to request incremental histories via
+ // the "start" query parameter.
+ history_offset: Integer;
+
+ // 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";
+
+ // 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:: PurseMergeTransaction
+
+ interface PurseMergeTransaction {
+ type: "MERGE";
+
+ // 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[];
}
@@ -1907,14 +2545,19 @@ denomination.
| CoinOldCoinRecoupTransaction
| CoinRecoupRefreshTransaction
| CoinPurseDepositTransaction
- | CoinPurseRefundTransaction;
-
+ | 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
@@ -1958,6 +2601,11 @@ denomination.
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
@@ -1987,6 +2635,11 @@ denomination.
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.
@@ -2017,6 +2670,11 @@ denomination.
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
@@ -2058,6 +2716,11 @@ denomination.
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
@@ -2082,6 +2745,11 @@ denomination.
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
@@ -2122,7 +2790,12 @@ denomination.
.. ts:def:: CoinPurseDepositTransaction
interface CoinPurseDepositTransaction {
- type: "PURSE_DEPOSIT";
+ 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.
@@ -2156,7 +2829,12 @@ denomination.
.. ts:def:: CoinPurseRefundTransaction
interface CoinPurseRefundTransaction {
- type: "PURSE_REFUND";
+ 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.
@@ -2169,11 +2847,6 @@ denomination.
// fee will be waived.
refund_fee: Amount;
- // Share of the purse fee charged to this coin.
- // The sum of all purse fee shares will match the
- // total purse fee.
- purse_fee_share: Amount;
-
// Public key of the purse that expired.
purse_pub: EddsaPublicKey;
@@ -2182,9 +2855,389 @@ denomination.
// of purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND``.
exchange_sig: EddsaSignature;
- // Public key used to sign 'exchange_sig'.
+ // Public key used to sign 'exchange_sig'.
+ exchange_pub: EddsaPublicKey;
+
+ }
+
+ .. ts:def:: CoinReserveOpenDepositTransaction
+
+ 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
+ // the deposit fee.
+ coin_contribution: Amount;
+
+ // Signature of the reserve open operation being paid for.
+ reserve_sig: EddsaSignature;
+
+ // Signature by the coin over a
+ // `TALER_ReserveOpenDepositSignaturePS` of
+ // purpose ``TALER_SIGNATURE_RESERVE_OPEN_DEPOSIT``.
+ coin_sig: EddsaSignature;
+
+ }
+
+
+.. _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
+ the digital coins.
+
+ **Request:**
+
+ The request body must be a `BatchDepositRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The operation succeeded, the exchange confirms that no double-spending took
+ 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.
+ :http:statuscode:`404 Not found`:
+ Either one of the denomination keys is not recognized (expired or invalid),
+ or the wire type is not recognized.
+ If a denomination key is unknown, the response will be
+ a `DenominationUnknownMessage`.
+ :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:
+
+ 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
+ `DenominationExpiredMessage`. Clients must evaluate
+ the error code provided to understand which of the
+ cases this is and handle it accordingly.
+
+ **Details:**
+
+ .. ts:def:: BatchDepositRequest
+
+ interface BatchDepositRequest {
+
+ // The merchant's account details.
+ merchant_payto_uri: string;
+
+ // The salt is used to hide the ``payto_uri`` from customers
+ // when computing the ``h_wire`` of the merchant.
+ wire_salt: WireSalt;
+
+ // SHA-512 hash of the contract of the merchant with the customer. Further
+ // details are never disclosed to the exchange.
+ h_contract_terms: HashCode;
+
+ // The list of coins that are going to be deposited with this Request.
+ coins: BatchDepositRequestCoin[];
+
+ // Timestamp when the contract was finalized.
+ timestamp: Timestamp;
+
+ // 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;
+
+ // 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
+ // 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;
+
+ // CAVEAT: THIS IS WORK IN PROGRESS
+ // (Optional) policy for the batch-deposit.
+ // This might be a refund, auction or escrow policy.
+ policy?: DepositPolicy;
+ }
+
+ .. ts:def:: BatchDepositRequestCoin
+
+ interface BatchDepositRequestCoin {
+ // EdDSA public key of the coin being deposited.
+ coin_pub: EddsaPublicKey;
+
+ // Hash of denomination RSA key with which the coin is signed.
+ denom_pub_hash: HashCode;
+
+ // Exchange's unblinded RSA signature of the coin.
+ ub_sig: DenominationSignature;
+
+ // Amount to be deposited, can be a fraction of the
+ // coin's total value.
+ contribution: Amount;
+
+ // Signature over `TALER_DepositRequestPS`, made by the customer with the
+ // `coin's private key <coin-priv>`.
+ 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:: DepositSuccess
+
+ 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;
+
+ // Timestamp when the deposit was received by the exchange.
+ exchange_timestamp: Timestamp;
+
+ // `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;
+ // 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
+ // deadline, or as soon as possible if the refund deadline is zero.
+ 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[];
}
@@ -2258,7 +3311,7 @@ the API during normal operation.
// Hash of the public key of the denomination the
// request relates to. Must be a CS denomination type.
- denom_pub_hash: Hash;
+ denom_pub_hash: HashCode;
}
@@ -2298,7 +3351,8 @@ the API during normal operation.
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_MELT_INSUFFICIENT_FUNDS`` or ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``).
+ (``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` or
+ ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``).
The response is `MeltForbiddenResponse` in both cases.
:http:statuscode:`410 Gone`:
The requested denomination key is not yet or no longer valid.
@@ -2329,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.
@@ -2338,6 +3392,10 @@ the API during normal operation.
// denominations is of type Clause-Schnorr.
rms?: RefreshMasterSeed;
+ // IFF the denomination has age restriction support, the client MUST
+ // provide the SHA256 hash of the age commitment of the coin.
+ // MUST be omitted otherwise.
+ age_commitment_hash?: AgeCommitmentHash;
}
For details about the HKDF used to derive the new coin private keys and
@@ -2460,15 +3518,19 @@ the API during normal operation.
// Signs over a `TALER_CoinLinkSignaturePS`.
link_sigs: EddsaSignature[];
+ // IFF the corresponding denomination has support for age restriction,
+ // the client MUST provide the original age commitment, i. e. the
+ // vector of public keys.
+ // The size of the vector MUST be the number of age groups as defined by the
+ // Exchange in the field ``.age_groups`` of the extension ``age_restriction``.
+ old_age_commitment?: Edx25519PublicKey[];
+
}
.. 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
@@ -2579,8 +3641,9 @@ in using this API.
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:`401 Unauthorized`:
+ :http:statuscode:`403 Forbidden`:
The coin's signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
The denomination key is unknown, or the blinded
coin is not known to have been withdrawn.
@@ -2623,6 +3686,11 @@ in using this API.
// Coin's blinding factor.
coin_blind_key_secret: DenominationBlindingKeySecret;
+ // Nonce that was used by the exchange to derive
+ // its private inputs from during withdraw. Only
+ // present if the cipher of the revoked denomination
+ // is of type Clause-Schnorr (CS).
+ cs_nonce?: CSNonce;
}
@@ -2658,8 +3726,9 @@ in using this API.
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:`401 Unauthorized`:
+ :http:statuscode:`403 Forbidden`:
The coin's signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
The denomination key is unknown, or the blinded
coin is not known to have been withdrawn.
@@ -2695,13 +3764,18 @@ in using this API.
// operation (see /csr-melt).
ewv: ExchangeWithdrawValue;
- // Signature of `TALER_RecoupRefreshRequestPS` created with
+ // Signature of `TALER_RecoupRequestPS` created with
// the `coin's private key <coin-priv>`.
coin_sig: EddsaSignature;
// Coin's blinding factor.
coin_blind_key_secret: DenominationBlindingKeySecret;
+ // Nonce that was used by the exchange to derive
+ // its private inputs from during withdraw. Only
+ // present if the cipher of the revoked denomination
+ // is of type Clause-Schnorr (CS).
+ cs_nonce?: CSNonce;
}
@@ -2819,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:**
@@ -2831,8 +3911,9 @@ typically also view the balance.)
executed. Hence the exchange does not yet have a wire transfer identifier. The
merchant should come back later and ask again.
The response body is a `TrackTransactionAcceptedResponse`.
- :http:statuscode:`401 Unauthorized`:
+ :http:statuscode:`403 Forbidden`:
A signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
The deposit operation is unknown to the exchange.
@@ -2851,9 +3932,6 @@ typically also view the balance.)
// The contribution of this coin to the total (without fees)
coin_contribution: Amount;
- // Total amount transferred.
- total_amount: Amount;
-
// Binary-only Signature_ with purpose ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE``
// over a `TALER_ConfirmWirePS`
// whereby the exchange affirms the successful wire transfer.
@@ -2870,13 +3948,23 @@ typically also view the balance.)
interface TrackTransactionAcceptedResponse {
- // Payment target that the merchant should
+ // Legitimization target that the merchant should
// use to check for its KYC status using
- // the ``/kyc-check/$PAYMENT_TARGET_UUID`` endpoint.
- payment_target_uuid: Integer;
+ // the ``/kyc-check/$REQUIREMENT_ROW/...`` endpoint.
+ // Optional, not present if the deposit has not
+ // yet been aggregated to the point that a KYC
+ // need has been evaluated.
+ requirement_row?: Integer;
+
+ // Current AML state for the target account. Non-zero
+ // values indicate that the transfer is blocked due to
+ // AML enforcement.
+ aml_decision: Integer;
// True if the KYC check for the merchant has been
- // satisfied.
+ // satisfied. False does not mean that KYC
+ // is strictly needed, unless also a
+ // legitimization_uuid is provided.
kyc_ok: boolean;
// Time by which the exchange currently thinks the deposit will be executed.
@@ -2891,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.
@@ -2901,7 +3989,7 @@ Refunds
:http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that the coin can now be refreshed. The response will include a `RefundSuccess` object.
- :http:statuscode:`401 Unauthorized`:
+ :http:statuscode:`403 Forbidden`:
Merchant signature is invalid.
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
@@ -2990,23 +4078,16 @@ Refunds
Wallet-to-wallet transfers
--------------------------
- .. note::
-
- This is a draft API that is not yet implemented.
+.. http:get:: /purses/$PURSE_PUB/merge
+.. http:get:: /purses/$PURSE_PUB/deposit
-
-.. http:GET:: /purses/$PURSE_PUB
-
- Obtain information about a purse. The request header must
- contain a *Purse-Request-Signature*. Endpoint used by
- the party that did not create the purse.
- TODO: maybe use POST to /purses/$PURSE_PUB/status instead?
+ Obtain information about a purse. Depending on the suffix,
+ the long-polling (if any) will wait for either a merge or
+ a deposit event.
**Request:**
- *Purse-Request-Signature*: The client must provide Base-32 encoded EdDSA signature made with ``$PURSE_PRIV``, affirming its authorization to download the purse status. The purpose used MUST be ``TALER_SIGNATURE_PURSE_STATUS_REQUEST``.
-
- :query merge_timeout_ms=NUMBER: *Optional.* If specified,
+ :query timeout_ms=NUMBER: *Optional.* If specified,
the exchange
will wait up to ``NUMBER`` milliseconds for completion
of a merge operation before sending the HTTP response.
@@ -3014,9 +4095,6 @@ Wallet-to-wallet transfers
the exchange
will wait up to ``NUMBER`` milliseconds for completion
of a deposit operation before sending the HTTP response.
- :query contract=BOOLEAN: *Optional.* If 'false' is specified,
- the exchange will not return the encrypted contract, saving
- bandwidth for clients that already know it.
**Response:**
@@ -3024,11 +4102,10 @@ Wallet-to-wallet transfers
The operation succeeded, the exchange provides details
about the purse.
The response will include a `PurseStatus` object.
- :http:statuscode:`401 Unauthorized`:
- The *Purse-Request-Signature* is invalid.
- This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
The purse is unknown to the exchange.
+ :http:statuscode:`410 Gone`:
+ The purse expired before the deposit or merge was completed.
**Details:**
@@ -3041,26 +4118,28 @@ Wallet-to-wallet transfers
// exceeds 'merge_value_after_fees', and a
// 'merge_request' exists for the purse, then the
// purse will (have been) merged with the account.
- total_deposit_amount: Amount;
+ balance: Amount;
- // Indicative time by which the purse expires
- // if it has not been merged into an account. At this
- // point, all of the deposits made will be auto-refunded.
+ // When does the purge expire.
purse_expiration: Timestamp;
- // Desired total amount to be merged into the reserve.
- // (excludes fees).
- merge_value_after_fees: Amount;
-
- // Indicative time at which the exchange is answering the
- // status request. Used as part of 'exchange_sig'.
- status_timestamp: Timestamp;
-
- // Deposit fees charged so far to all deposited coins.
- deposit_fees: Amount;
-
- // SHA-512 hash of the contact of the purse.
- h_contract_terms: HashCode;
+ // Time of the merge, missing if "never".
+ merge_timestamp?: Timestamp;
+
+ // Time of the deposits being complete, missing if "never".
+ // Note that this time may not be "stable": once sufficient
+ // deposits have been made, is "now" before the purse
+ // expiration, and otherwise set to the purse expiration.
+ // However, this should also not be relied upon. The key
+ // property is that it is either "never" or in the past.
+ deposit_timestamp?: Timestamp;
+
+ // Time when the purse expires and
+ // funds that were not merged are refunded
+ // on the deposited coins.
+ // FIXME: Document the exchange protocol version
+ // in which this field became available.
+ purse_expiration: Timestamp;
// EdDSA signature of the exchange over a
// `TALER_PurseStatusResponseSignaturePS`
@@ -3071,29 +4150,17 @@ Wallet-to-wallet transfers
// EdDSA public key exchange used for 'exchange_sig'.
exchange_pub: EddsaPublicKey;
- // EdDSA public key used to approve merges of this purse.
- merge_pub: EddsaPublicKey;
-
- // AES-GCM Encrypted contract terms using encryption
- // key derived from DH of 'contract_pub' and the 'purse_pub'.
- // Optional, may be omitted if not desired by the client.
- e_contract_terms?: string;
-
- // If a merge request was received, information about the
- // merge request. Omitted if the purse has not yet received
- // a merge request.
- merge_request?: MergeRequest;
-
}
-.. http:POST:: /purses/$PURSE_PUB/deposit
- Deposit money into a purse. Endpoint used by the buyer.
+.. http:post:: /purses/$PURSE_PUB/create
+
+ Create a purse by depositing money into it. First step of a PUSH payment.
**Request:**
- The request body must be a `PurseRequest` object.
+ The request body must be a `PurseCreate` object.
**Response:**
@@ -3101,49 +4168,55 @@ Wallet-to-wallet transfers
The operation succeeded, the exchange confirms that all
coins were deposited into the purse.
The response will include a `PurseDepositSuccess` object.
- :http:statuscode:`202 Accepted`:
- The payment was accepted, but insufficient to reach the
- specified purse balance. If an encrypted contract was
- provided, it will have been stored in the database.
- The client should make further
- purse deposits before the expiration deadline.
- The response will include a `PurseDepositAccepted` object.
- :http:statuscode:`401 Unauthorized`:
- A coin signature is invalid. The response will
- include a `PurseDepositSignatureErrorDetail`
:http:statuscode:`403 Forbidden`:
- The server is denying the operation as a purse with a
- different contract or total amount already exists.
- This response comes with a standard `PurseConflict` response.
- :http:statuscode:`404 Not found`:
- FIXME: when exactly does this happen?
+ A coin, denomination or contract signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not Found`:
+ The denomination of one of the coins is unknown to the exchange.
:http:statuscode:`409 Conflict`:
The deposit operation has either failed because a 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
+ previously used with a different denomination, or because a purse with
+ the same public key but different meta data was created previously.
+ Which case it is
can be decided by looking at the error code
- (``TALER_EC_EXCHANGE_DEPOSIT_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 `PurseDepositDoubleSpendError`.
- If the value of all successful coins is below the purse fee,
- the exchange may not setup the purse at all. The encrypted
- contract will not have been associated with the purse if this
- status code is returned. However, all coins that were not
- double-spent will have been deposited into the purse.
+ (``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` or
+ ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY`` or
+ ``TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA`` or
+ ``TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA`` or
+ ``TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA``).
+ The specific fields of the response depend on the error code
+ and include the signatures (and what was signed over) proving the
+ conflict.
:http:statuscode:`425 Too Early`:
This response type is used if the given purse expiration time
is too far in the future (at least from the perspective
of the exchange). Thus, retrying at a later time may
- succeed. The client should look at the ``Date:`` header of the response to see if a minor time difference is to blame and possibly adjust the request accordingly.
+ succeed. The client should look at the ``Date:`` header
+ of the response to see if a minor time difference is to
+ blame and possibly adjust the request accordingly.
+ (Note: this status code is not yet used.)
**Details:**
- .. ts:def:: PurseRequest
+ .. ts:def:: PurseCreate
- interface PurseRequest {
+ interface PurseCreate {
+
+ // Total value of the purse, excluding fees.
+ amount: Amount;
+
+ // Minimum age required for all coins deposited into the purse.
+ min_age: Integer;
+
+ // Optional encrypted contract, in case the buyer is
+ // proposing the contract and thus establishing the
+ // purse with the payment.
+ econtract?: EncryptedContract;
+
+ // EdDSA public key used to approve merges of this purse.
+ merge_pub: EddsaPublicKey;
// EdDSA signature of the purse over a
// `TALER_PurseRequestSignaturePS`
@@ -3153,65 +4226,42 @@ Wallet-to-wallet transfers
// (amount, h_contract_terms, expiration).
purse_sig: EddsaSignature;
- // EdDSA public key used to approve merges of this purse.
- merge_pub: EddsaPublicKey;
-
- // Total amount to be paid into the purse.
- // Clients may make several requests, i.e. if a
- // first request failed with a double-spending error.
- // The exchange will confirm the creation of the
- // purse once the amount given here is reached.
- merge_value_after_fees: Amount;
-
// SHA-512 hash of the contact of the purse.
h_contract_terms: HashCode;
- // Minimum age of deposits made into the purse
- minimum_age: Integer;
-
+ // Array of coins being deposited into the purse.
+ // Maximum length is 128.
+ deposits: PurseDeposit[];
+
// 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 will be auto-refunded.
purse_expiration: Timestamp;
- // Optional encrypted contract, in case the buyer is
- // proposing the contract and thus establishing the
- // purse with the payment.
- contract?: EncryptedContract;
-
- // Array of coins being deposited into the purse.
- // Maximum length is 128.
- deposits: PurseDeposit[];
}
- .. ts:def:: EncryptedContract
+ .. ts:def:: EncryptedContract
interface EncryptedContract {
- // ECDH contract_public key used to encrypt the contract.
- // Optional as the contract terms may already be known
- // to the exchange or the other wallet from a different
- // interaction.
- contract_pub: TALER_EcdhEphemeralPublicKeyP;
+ // Encrypted contract.
+ econtract: string;
+
+ // Signature over the (encrypted) contract.
+ econtract_sig: EddsaSignature;
+
+ // Ephemeral public key for the DH operation to decrypt the encrypted contract.
+ contract_pub: EddsaPublicKey;
- // AES-GCM Encrypted contract terms using encryption
- // key derived from DH of ``contract_pub`` and the ``purse_pub``.
- // Optional as the contract terms may already be known
- // to the exchange or the other wallet from a different
- // interaction.
- e_contract_terms: string;
}
.. ts:def:: PurseDeposit
interface PurseDeposit {
- // Public key of the coin being deposited into the purse.
- coin_pub: EddsaPublicKey;
-
// Amount to be deposited, can be a fraction of the
// coin's total value.
- contribution: Amount;
+ amount: Amount;
// Hash of denomination RSA key with which the coin is signed.
denom_pub_hash: HashCode;
@@ -3219,23 +4269,32 @@ Wallet-to-wallet transfers
// Exchange's unblinded RSA signature of the coin.
ub_sig: DenominationSignature;
+ // Age commitment for the coin, if the denomination is age-restricted.
+ age_commitment?: AgeCommitment;
+
+ // Attestation for the minimum age, if the denomination is age-restricted.
+ attest?: Attestation;
+
// Signature over `TALER_PurseDepositSignaturePS`
// of purpose ``TALER_SIGNATURE_WALLET_PURSE_DEPOSIT``
// made by the customer with the
// `coin's private key <coin-priv>`.
coin_sig: EddsaSignature;
+ // Public key of the coin being deposited into the purse.
+ coin_pub: EddsaPublicKey;
+
}
.. ts:def:: PurseDepositSuccess
interface PurseDepositSuccess {
- // Total amount paid into the purse.
- total_purse_amount: Amount;
+ // Total amount deposited into the purse so far (without fees).
+ total_deposited: Amount;
- // Total deposit fees charged.
- total_deposit_fees: Amount;
+ // Time at the exchange.
+ exchange_timestamp: Timestamp;
// EdDSA signature of the exchange affirming the payment,
// of purpose ``TALER_SIGNATURE_PURSE_DEPOSIT_CONFIRMED``
@@ -3249,39 +4308,27 @@ Wallet-to-wallet transfers
}
- .. ts:def:: PurseDepositAccepted
-
- interface PurseDepositAccepted {
-
- // Total amount paid so far into the purse, in this
- // and previous requests.
- total_amount_deposited: Amount;
-
- // Total amount contributed by the current request.
- total_amount_contributed: Amount;
-
- }
-
.. ts:def:: PurseConflict
- // Union discriminated by the "type" field.
+ // Union discriminated by the "code" field.
type PurseConflict =
- | PurseMergeConflict
- | PurseRequestConflict;
+ | DepositDoubleSpendError
+ | PurseCreateConflict
+ | PurseDepositConflict
+ | PurseContractConflict;
- .. ts:def:: PurseMergeConflict
+ .. ts:def:: PurseCreateConflict
- interface PurseMergeConflict {
- type: "MERGE";
-
- // SHA-512 hash of the contact of the purse.
- h_contract_terms: HashCode;
+ interface PurseCreateConflict {
+ // Must be equal to TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA
+ code: Integer;
- // Hash of the wire details of the reserve.
- h_wire: HashCode;
+ // Total amount to be merged into the reserve.
+ // (excludes fees).
+ amount: Amount;
- // Reserve merging the purse.
- reserve_pub: EddsaPublicKey;
+ // Minimum age required for all coins deposited into the purse.
+ min_age: Integer;
// Indicative time by which the purse should expire
// if it has not been merged into an account. At this
@@ -3289,100 +4336,94 @@ Wallet-to-wallet transfers
// auto-refunded.
purse_expiration: Timestamp;
- // When was the merge request generated.
- merge_timestamp: Timestamp;
-
- // Total amount to be merged into the reserve.
- // (excludes fees).
- merge_value_after_fees: Amount;
-
// EdDSA signature of the purse over
// `TALER_PurseMergeSignaturePS` of
// purpose ``TALER_SIGNATURE_WALLET_PURSE_MERGE``
// confirming that the
// above details hold for this purse.
purse_sig: EddsaSignature;
+
+ // 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;
}
- .. ts:def:: PurseRequestConflict
+ .. ts:def:: PurseDepositConflict
- interface PurseRequestConflict {
- type: "REQUEST";
+ interface PurseDepositConflict {
+ // Must be equal to TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA
+ code: Integer;
- // SHA-512 hash of the contact of the purse.
- h_contract_terms: HashCode;
+ // Public key of the coin being deposited into the purse.
+ coin_pub: EddsaPublicKey;
- // 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;
+ // Signature over `TALER_PurseDepositSignaturePS`
+ // of purpose ``TALER_SIGNATURE_WALLET_PURSE_DEPOSIT``
+ // made by the customer with the
+ // `coin's private key <coin-priv>`.
+ coin_sig: EddsaSignature;
- // Total amount to be paid into the purse as per
- // the previous request.
- total_purse_amount: Amount;
+ // Target exchange URL for the purse. Not present for the
+ // same exchange.
+ partner_url?: string;
- // EdDSA signature of the purse over
- // `TALER_PurseRequestSignaturePS` of
- // purpose ``TALER_SIGNATURE_PURSE_REQUEST``
- // confirming that the
- // above details hold for this purse.
- purse_sig: EddsaSignature;
+ // Amount to be contributed to the purse by this coin.
+ amount: Amount;
}
- .. ts:def:: PurseDepositDoubleSpendError
+ .. ts:def:: PurseContractConflict
- interface DepositDoubleSpendError {
- // Taler error code.
- code: number;
+ interface PurseContractConflict {
+ // Must be equal to TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA
+ code: Integer;
- // Human-readable description of the error, i.e. "insufficient funds".
- hint?: string;
+ // Hash of the encrypted contract.
+ h_econtract: HashCode;
+
+ // Signature over the contract.
+ econtract_sig: EddsaSignature;
- // Total amount contributed by the current request.
- // Note that some coins may have been successfully
- // deposited into the purse, so the total amount
- // from these coins is listed here.
- total_amount_contributed: Amount;
+ // Ephemeral public key for the DH operation to decrypt the contract.
+ contract_pub: EddsaPublicKey;
- // Public keys of coins that could not be deposited
- // into the purse, mapped to the coin's histories.
- coin_map: EddsaPublicKey -> CoinSpendHistoryItem[];
}
- .. ts:def:: PurseDepositSignatureErrorDetail
- interface PurseDepositSignatureErrorDetail {
- // Taler error code, summarizing the problem.
- // Note that for problems about specific
- // coins, the 'coin_error_map' should be consulted.
- // The 'coin_error_map' will be empty if the
- // 'purse_sig' was invalid. In this case,
- // the coins will not even have been checked by
- // the exchange.
- code: number;
+.. http:delete:: /purses/$PURSE_PUB
- // Human-readable description of the error, i.e. "invalid signature".
- hint?: string;
+ Delete a purse that is unmerged and not yet expired. Refunds any money that
+ is already in the purse.
- // Total amount contributed by the current request.
- // Note that some coins may have been successfully
- // deposited into the purse, so the total amount
- // from these coins is listed here.
- total_amount_contributed: Amount;
+ **Request:**
- // Public keys of coins that could not be deposited
- // into the purse, mapped to the coin's histories.
- coin_error_map: EddsaPublicKey -> ErrorDetail[];
- }
+ The request body must be empty, as recommended for HTTP delete in general.
+ To authorize the request, the header must contain a
+ ``Taler-Purse-Signature: $PURSE_SIG`` where ``$PURSE_SIG`` is the Crockford base32-encoded
+ EdDSA signature of purpose TALER_SIGNATURE_WALLET_PURSE_DELETE.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The operation succeeded, the exchange confirms that the purse
+ was deleted.
+ :http:statuscode:`403 Forbidden`:
+ The signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not Found`:
+ The purse is not known. Might have already been deleted previously.
+ :http:statuscode:`409 Conflict`:
+ It is too late to delete the purse, its fate (merge or expiration)
+ 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 seller.
+ the account. Endpoint to be used by the receiver of a PUSH payment.
**Request:**
@@ -3394,25 +4435,26 @@ Wallet-to-wallet transfers
The operation succeeded, the exchange confirms that the
funds were merged into the account.
The response will include a `MergeSuccess` object.
- :http:statuscode:`202 Accepted`:
- The operation succeeded, the exchange confirms that the
- merge request is valid. Alas, the purse was still not
- funded and thus the actual merge is delayed.
- The response will include a `MergeAccepted` object.
- :http:statuscode:`401 Unauthorized`:
- Account signature is invalid.
+ :http:statuscode:`402 Payment Required`:
+ The purse is not yet full and more money needs to be deposited
+ before the merge can be made.
+ :http:statuscode:`403 Forbidden`:
+ The signature of the merge request or the reserve was invalid.
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
- The refund operation failed as we could not find the purse.
+ The merge operation failed as we could not find the purse
+ or the partner exchange.
This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`409 Conflict`:
+ The purse was already merged into a different reserve.
+ The response will include a `MergeConflict` object.
:http:statuscode:`410 Gone`:
The purse has already expired and thus can no longer be merged.
This response comes with a standard `ErrorDetail` response.
- :http:statuscode:`429 Too Many Requests`:
- This account is not at this exchange, has not yet passed the
- KYC checks, or it has exceeded the number of open purses.
- The client must include payment to create another purse or
- wait until existing purses have expired.
+ :http:statuscode:`451 Unavailable For Legal Reasons`:
+ This account has not yet passed the KYC checks.
+ The client must pass KYC checks before proceeding with the merge.
+ The response will be an `KycNeededRedirect` object.
**Details:**
@@ -3429,7 +4471,7 @@ Wallet-to-wallet transfers
// Must be of purpose ``TALER_SIGNATURE_ACCOUNT_MERGE``
reserve_sig: EddsaSignature;
- // EdDSA signature of the purse private key affirming the merge
+ // EdDSA signature of the merge private key affirming the merge
// over a `TALER_PurseMergeSignaturePS`.
// Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE``.
merge_sig: EddsaSignature;
@@ -3462,21 +4504,31 @@ Wallet-to-wallet transfers
}
- .. ts:def:: MergeAccepted
+ .. ts:def:: MergeConflict
- interface MergeAccepted {
-
- // Current balance in the purse.
- balance: Amount;
+ interface MergeConflict {
+
+ // Client-side timestamp of when the merge request was made.
+ merge_timestamp: Timestamp;
+
+ // EdDSA signature of the purse private key affirming the merge
+ // over a `TALER_PurseMergeSignaturePS`.
+ // Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE``.
+ merge_sig: EddsaSignature;
+ // Base URL of the exchange receiving the payment, only present
+ // if the exchange hosting the reserve is not this exchange.
+ partner_url?: string;
+
+ // Public key of the reserve that the purse was merged into.
+ reserve_pub: EddsaPublicKey;
}
-.. http:POST:: /reserves/$RESERVE_PUB/purse
+.. http:post:: /reserves/$RESERVE_PUB/purse
- Create purse for an account.
- Endpoint to be used by the seller.
+ Create purse for an account. First step of a PULL payment.
**Request:**
@@ -3487,16 +4539,33 @@ Wallet-to-wallet transfers
:http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that the
purse was allocated.
- The response will include a `MergePending` object.
- :http:statuscode:`401 Unauthorized`:
- Account signature is invalid.
- This response comes with a standard `ErrorDetail` response.
+ The response will include a `PurseDepositSuccess` object.
:http:statuscode:`402 Payment Required`:
The account needs to contain more funding to create more purses.
This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`403 Forbidden`:
+ Account or contract signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
- The refund operation failed as we could not find the purse.
+ The purse creation operation failed as we could not find the reserve.
This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`409 Conflict`:
+ The purse creation failed because a purse with
+ the same public key but different meta data was
+ created previously. Which specific conflict it is
+ can be decided by looking at the error code
+ (``TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA`` or
+ ``TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA`` or
+ ``TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA``).
+ The specific fields of the response depend on the error code
+ and include the signatures (and what was signed over) proving the
+ conflict.
+ The response will be a `PurseConflict` response
+ (but not a `DepositDoubleSpendError`).
+ :http:statuscode:`451 Unavailable For Legal Reasons`:
+ This account has not yet passed the KYC checks.
+ The client must pass KYC checks before proceeding with the merge.
+ The response will be an `KycNeededRedirect` object.
**Details:**
@@ -3504,16 +4573,47 @@ Wallet-to-wallet transfers
interface ReservePurseRequest {
- // EdDSA signature of the account/reserve affirming the merge
- // over a `TALER_XXX`.
- // Must be of purpose ``TALER_SIGNATURE_XXX``
- reserve_sig: EddsaSignature;
-
// Minimum amount that must be credited to the reserve, that is
// the total value of the purse minus the deposit fees.
// If the deposit fees are lower, the contribution to the
// reserve can be higher!
- merge_value_after_fees: Amount;
+ purse_value: Amount;
+
+ // Minimum age required for all coins deposited into the purse.
+ min_age: Integer;
+
+ // Purse fee the reserve owner is willing to pay
+ // for the purse creation. Optional, if not present
+ // the purse is to be created from the purse quota
+ // of the reserve.
+ purse_fee: Amount;
+
+ // Optional encrypted contract, in case the buyer is
+ // proposing the contract and thus establishing the
+ // purse with the payment.
+ econtract?: EncryptedContract;
+
+ // EdDSA public key used to approve merges of this purse.
+ merge_pub: EddsaPublicKey;
+
+ // EdDSA signature of the purse private key affirming the merge
+ // over a `TALER_PurseMergeSignaturePS`.
+ // Must be of purpose ``TALER_SIGNATURE_PURSE_MERGE``.
+ merge_sig: EddsaSignature;
+
+ // EdDSA signature of the account/reserve affirming the merge.
+ // Must be of purpose ``TALER_SIGNATURE_WALLET_ACCOUNT_MERGE``
+ reserve_sig: EddsaSignature;
+
+ // Purse public key.
+ purse_pub: EddsaPublicKey;
+
+ // EdDSA signature of the purse over
+ // `TALER_PurseRequestSignaturePS` of
+ // purpose ``TALER_SIGNATURE_PURSE_REQUEST``
+ // confirming that the
+ // above details hold for this purse.
+ purse_sig: EddsaSignature;
// SHA-512 hash of the contact of the purse.
h_contract_terms: HashCode;
@@ -3525,32 +4625,135 @@ Wallet-to-wallet transfers
// if it has not been paid.
purse_expiration: Timestamp;
- // Optional encrypted contract, in case the seller is
- // proposing the contract and thus establishing the
- // purse with the payment.
- contract?: EncryptedContract;
+ }
+
+
+.. http:post:: /purses/$PURSE_PUB/deposit
+
+ Deposit money into a purse. Used by the buyer for a PULL payment.
+
+ **Request:**
+
+ The request body must be a `PurseDeposits` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The operation succeeded, the exchange confirms that all
+ coins were deposited into the purse.
+ The response will include a `PurseDepositSuccess` object.
+ :http:statuscode:`403 Forbidden`:
+ A coin or denomination signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The purse is unknown.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`409 Conflict`:
+ The deposit operation has either failed because a 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`` or
+ ``TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA``).
+ This response comes with a standard `PurseConflict` response
+ (alas some cases are impossible).
+ :http:statuscode:`410 Gone`:
+ The purse has expired.
+
+
+ **Details:**
+
+ .. ts:def:: PurseDeposits
+
+ interface PurseDeposits {
+ // Array of coins to deposit into the purse.
+ deposits: PurseDeposit[];
}
- .. ts:def:: MergePending
+ .. ts:def:: PurseDeposit
- interface MergePending {
+ interface PurseDeposit {
- // The number of remaining purses that can still be opened
- // under the given account.
- remaining_purses: Integer;
+ // Amount to be deposited, can be a fraction of the
+ // coin's total value.
+ amount: Amount;
+
+ // Hash of denomination RSA key with which the coin is signed.
+ denom_pub_hash: HashCode;
+
+ // Exchange's unblinded RSA signature of the coin.
+ ub_sig: DenominationSignature;
+
+ // Age commitment for the coin, if the denomination is age-restricted.
+ age_commitment?: AgeCommitment;
+
+ // Attestation for the minimum age, if the denomination is age-restricted.
+ attest?: Attestation;
+
+ // Signature over `TALER_PurseDepositSignaturePS`
+ // of purpose ``TALER_SIGNATURE_WALLET_PURSE_DEPOSIT``
+ // made by the customer with the
+ // `coin's private key <coin-priv>`.
+ coin_sig: EddsaSignature;
+
+ // Public key of the coin being deposited into the purse.
+ coin_pub: EddsaPublicKey;
}
-
--- TODO: API to deposit into existing purse
-
+ .. ts:def:: PurseDepositSuccess
+
+ interface PurseDepositSuccess {
+
+ // Total amount paid into the purse.
+ total_deposited: Amount;
+
+ // Total amount expected in the purse.
+ purse_value_after_fees: Amount;
+
+ // Time at which the deposit came into effect.
+ exchange_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 will be auto-refunded.
+ purse_expiration: Timestamp;
+
+ // SHA-512 hash of the contact of the purse.
+ h_contract_terms: HashCode;
+
+ // EdDSA signature of the exchange affirming the payment,
+ // of purpose ``TALER_SIGNATURE_PURSE_DEPOSIT_CONFIRMED``
+ // over a `TALER_PurseDepositConfirmedSignaturePS`.
+ // Signs over the above and the purse public key and
+ // the hash of the contract terms.
+ exchange_sig: EddsaSignature;
+
+ // public key used to create the signature.
+ exchange_pub: EddsaPublicKey;
+
+ }
+
+ .. ts:def:: AgeCommitment
+
+ // AgeCommitment is an array of public keys, one for each age group of the
+ // age-restricted denomination.
+ type AgeCommitment = Edx25519PublicKey[];
+
+ .. ts:def:: Attestation
+
+ // An attestation for a minimum age is an Edx25519 signature of the age
+ // with purpose ``TALER_SIGNATURE_WALLET_AGE_ATTESTATION``.
+ type Attestation = string;
.. _exchange_wads:
+----
Wads
-^^^^
+----
.. note::
@@ -3561,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.
@@ -3649,14 +4852,18 @@ wallet-to-wallet payments. Only another exchange should access this endpoint.
KYC status updates
------------------
- .. note::
-
- This is a draft API that is not yet implemented.
-
+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
+.. 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:**
@@ -3664,13 +4871,17 @@ KYC status updates
**Response:**
- :http:statuscode:`200 Ok`:
- A KYC ID was created.
- The response will be a `WalletKycUuid` object.
:http:statuscode:`204 No Content`:
- KYC is disabled at this exchange.
- :http:statuscode:`401 Unauthorized`:
+ KYC is disabled at this exchange, or the balance
+ is below the threshold that requires KYC, or this
+ wallet already satisfied the KYC check for the
+ given balance.
+ :http:statuscode:`403 Forbidden`:
The provided signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`451 Unavailable for Legal Reasons`:
+ The wallet must undergo a KYC check. A KYC ID was created.
+ The response will be a `WalletKycUuid` object.
**Details:**
@@ -3678,6 +4889,11 @@ KYC status updates
interface WalletKycRequest {
+ // Balance threshold (not necessarily exact balance)
+ // to be crossed by the wallet that (may) trigger
+ // additional KYC requirements.
+ balance: Amount;
+
// EdDSA signature of the wallet affirming the
// request, must be of purpose
// ``TALER_SIGNATURE_WALLET_ACCOUNT_SETUP``
@@ -3694,22 +4910,31 @@ KYC status updates
// UUID that the wallet should use when initiating
// the KYC check.
- payment_target_uuid: number;
+ requirement_row: number;
+
+ // Hash of the payto:// account URI for the wallet.
+ h_payto: PaytoHash;
}
-.. http:GET:: /kyc-check/$PAYMENT_TARGET_UUID
+.. http:get:: /kyc-check/$REQUIREMENT_ROW/$H_PAYTO/$USERTYPE
- 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 using OAuth.
+ 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.
+
+ 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:**
- :query h_payto=HASH: Specifies the hash of the payto:// URI of the payment
- target. Weak security check used to authorize the status request.
:query timeout_ms=NUMBER: *Optional.* If specified, the exchange will
wait up to ``timeout_ms`` milliseconds if the payment target
is currently not legitimized. Ignored if the payment target
@@ -3721,26 +4946,41 @@ KYC status updates
:http:statuscode:`200 Ok`:
The KYC operation succeeded, the exchange confirms that the
- payment target is now authorized to transact.
+ payment target already authorized to transact.
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/`` 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.
- :http:statuscode:`401 Unauthorized`:
+ 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.**
+ :http:statuscode:`403 Forbidden`:
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:**
.. ts:def:: AccountKycStatus
- interface AccountKycStatus {
+ interface AccountKycStatus {
+
+ // Details about the KYC check that the user
+ // passed.
+ kyc_details: KycDetails;
// Current time of the exchange, used as part of
// what the exchange signs over.
@@ -3754,50 +4994,485 @@ 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
- interface AccountKycRedirect {
+ interface AccountKycRedirect {
// URL that the user should open in a browser to
// 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
-.. http:GET:: /kyc-proof/$H_PAYTO
+ interface AccountAmlBlocked {
- Update KYC status of a particular payment target. Provides
- the token to the exchange that allows it to verify that the
- user has completed the KYC process. The HTTP method must be
- a GET due to requirements from the OAuth 2.0 protocol.
+ // 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
+
+ // Object that specifies which KYC checks are satisfied.
+ interface KycDetails {
+
+ // Keys are the names of the check(s).
+ // The values are for now always empty objects.
+
+ }
+
+.. http:get:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO
+
+ 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".
+
+ 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:**
+ Details on the request depend on the specific KYC logic
+ that was used.
+
+ If the KYC plugin logic is OAuth 2.0, the query parameters are:
+
:query code=CODE: OAuth 2.0 code argument.
- :query state=CODE: OAuth 2.0 state 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.
The browser is redirected to a human-readable
page configured by the exchange operator.
:http:statuscode:`401 Unauthorized`:
- The provided OAuth 2.0 authentication token is invalid.
+ The provided authorization token is invalid.
:http:statuscode:`404 Not found`:
The payment target is unknown.
:http:statuscode:`502 Bad Gateway`:
- The exchange received an invalid reply from the OAuth-based
+ The exchange received an invalid reply from the
legitimization service.
:http:statuscode:`504 Gateway Timeout`:
- The exchange did not receive a reply from the OAuth legitimization
+ The exchange did not receive a reply from the legitimization
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/*
+
+ 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:**
+
+ Details on the request depend on the specific KYC logic
+ that was used.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The operation succeeded.
+ :http:statuscode:`404 Not found`:
+ The specified logic is unknown.
+
+
+---------------
+Reserve control
+---------------
+
+This section describes the reserve control API which can be used to (1)
+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::
+
+ This section is about a proposed API. It is not implemented. See also DD 31.
+
+.. http:post:: /reserves/$RESERVE_PUB/open
+
+ Request keeping a reserve open for invoicing.
+
+ **Request:**
+
+ The request body must be a `ReserveOpenRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `ReserveOpenResponse` object.
+ :http:statuscode:`402 Payment Required`:
+ The exchange responds with a `ReserveOpenFailure` object when
+ the payment offered is insufficient for the requested operation.
+ :http:statuscode:`403 Forbidden`:
+ The *TALER_SIGNATURE_WALLET_RESERVE_OPEN* signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The reserve key does not belong to a reserve known to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The balance of the reserve or of a coin was insufficient.
+ 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`` or
+ ``TALER_EC_EXCHANGE_OPEN_INSUFFICIENT_FUNDS``).
+ The specific fields of the response depend on the error code
+ and include the signatures (and what was signed over) proving the
+ conflict.
+ The response is `WithdrawError` object or a `DepositDoubleSpendError`
+ depending on the error type.
+ :http:statuscode:`451 Unavailable For Legal Reasons`:
+ This account has not yet passed the KYC checks.
+ The client must pass KYC checks before the reserve can be opened.
+ The response will be an `KycNeededRedirect` object.
+
+ **Details:**
+
+ .. ts:def:: ReserveOpenRequest
+
+ interface ReserveOpenRequest {
+ // Signature of purpose
+ // ``TALER_SIGNATURE_WALLET_RESERVE_OPEN`` over
+ // a `TALER_ReserveOpenPS`.
+ reserve_sig: EddsaSignature;
+
+ // Array of payments made towards the cost of the
+ // operation.
+ payments: OpenPaymentDetail[];
+
+ // Amount to be paid from the reserve for this
+ // operation.
+ reserve_payment: Amount;
+
+ // 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 (with a status code of 400).
+ request_timestamp: Timestamp;
+
+ // Desired new expiration time for the reserve.
+ // If the reserve would expire before this time,
+ // the exchange will charge account fees (and
+ // possibly KYC fees) until the expiration time
+ // exceeds this timestamp. Note that the exchange
+ // will refuse requests (with a status code of 400)
+ // if the time is so far in the future that the
+ // fees are not yet known (see /keys).
+ reserve_expiration: Timestamp;
+
+ // Desired open purse limit. Can be used to pay the
+ // annual account fee more than once to get a larger
+ // purse limit.
+ purse_limit: Integer;
+
+ }
+
+ .. ts:def:: ReserveOpenResponse
+
+ interface ReserveOpenResponse {
+ // Transaction cost for extending the expiration time.
+ // Excludes KYC fees.
+ open_cost: Amount;
+
+ // Current expiration time for the reserve.
+ reserve_expiration: Timestamp;
+ }
+
+ .. ts:def:: ReserveOpenFailure
+
+ interface ReserveOpenFailure {
+ // Transaction cost that should have been paid
+ // to extending the reserve as requested.
+ // Excludes KYC fees.
+ open_cost: Amount;
+
+ // Remaining expiration time for the reserve.
+ reserve_expiration: Timestamp;
+ }
+
+ .. ts:def:: OpenPaymentDetail
+
+ interface OpenPaymentDetail {
+
+ // Contribution of this coin to the overall amount.
+ // Can be a fraciton of the coin's total value.
+ amount: Amount;
+
+ // Hash of denomination RSA key with which the coin is signed.
+ denom_pub_hash: HashCode;
+
+ // Exchange's unblinded RSA signature of the coin.
+ ub_sig: DenominationSignature;
+
+ // Age commitment for the coin, if the denomination is age-restricted.
+ age_commitment?: AgeCommitment;
+
+ // Signature over `TALER_ReserveOpenDepositSignaturePS`
+ // of purpose ``TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT``
+ // made by the customer with the
+ // `coin's private key <coin-priv>`.
+ coin_sig: EddsaSignature;
+
+ // Public key of the coin being used to pay for
+ // opening the reserve.
+ coin_pub: EddsaPublicKey;
+
+ }
+
+
+.. http:get:: /reserves-attest/$RESERVE_PUB
+
+ Request list of available KYC attributes about the owner of a reserve.
+ Used as a preliminary step to find out which subsets of attributes the
+ exchange could provide signatures over.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `ReserveKycAttributes` object.
+ :http:statuscode:`404 Not found`:
+ The reserve key does not belong to a reserve known to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The exchange does not have the requested KYC information.
+
+ **Details:**
+
+ .. ts:def:: ReserveKycAttributes
+
+ interface ReserveKycAttributes {
+ // Array of KYC attributes available. The attribute names
+ // listed are expected to be from the respective GANA
+ // registry.
+ details: string[];
+ }
+
+
+.. http:post:: /reserves-attest/$RESERVE_PUB
+
+ Request signed KYC information about the owner of a reserve.
+ This can be used by a reserve owner to include a proof
+ of their identity in invoices.
+
+ **Request:**
+
+ The request body must be a `ReserveAttestRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `ReserveAttestResponse` object.
+ :http:statuscode:`403 Forbidden`:
+ The *TALER_SIGNATURE_WALLET_KYC_DETAILS* signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The reserve key does not belong to a reserve known to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The exchange does not have the requested KYC information.
+
+ **Details:**
+
+ .. ts:def:: ReserveAttestRequest
+
+ interface ReserveAttestRequest {
+ // Signature of purpose
+ // ``TALER_SIGNATURE_WALLET_ATTEST_DETAILS`` over
+ // a `TALER_WalletReserveAttestRequestSignaturePS`.
+ reserve_sig: EddsaSignature;
+
+ // Client's time for the request.
+ request_timestamp: Timestamp;
+
+ // Array of KYC attributes requested.
+ details: string[];
+ }
+
+ .. ts:def:: ReserveAttestResponse
+
+ interface ReserveAttestResponse {
+ // Signature of purpose
+ // ``TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS`` over
+ // a `TALER_ExchangeAttestPS`.
+ exchange_sig: EddsaSignature;
+
+ // Exchange public key used to create the
+ // signature.
+ exchange_pub: EddsaPublicKey;
+
+ // Time when the exchange created the signature.
+ exchange_timestamp: Timestamp;
+
+ // Expiration time for the provided information.
+ expiration_time: Timestamp;
+
+ // KYC details (key-value pairs) as requested.
+ // The keys will match the elements of the
+ // ``details`` array from the request.
+ attributes: Object;
+ }
+
+
+.. http:post:: /reserves/$RESERVE_PUB/close
+
+ Force immediate closure of a reserve. Does not actually
+ delete the reserve or the KYC data, but merely forces
+ the reserve's current balance to be wired back to the
+ account where it originated from, or another account of
+ the user's choosing if they performed the required KYC
+ check and designated another target account.
+
+ **Request:**
+
+ The request body must be a `ReserveCloseRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange responds with a `ReserveCloseResponse` object.
+ :http:statuscode:`403 Forbidden`:
+ The *TALER_SIGNATURE_WALLET_RESERVE_CLOSE* signature is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The reserve key does not belong to a reserve known to the exchange.
+ :http:statuscode:`409 Conflict`:
+ No target account was given, and the exchange does not know an
+ origin account for this reserve.
+ :http:statuscode:`451 Unavailable For Legal Reasons`:
+ This account has not yet passed the KYC checks, hence wiring
+ funds to a non-origin account is not allowed.
+ The client must pass KYC checks before the reserve can be opened.
+ The response will be an `KycNeededRedirect` object.
+
+ **Details:**
+
+ .. ts:def:: ReserveCloseRequest
+
+ interface ReserveCloseRequest {
+ // Signature of purpose
+ // ``TALER_SIGNATURE_WALLET_RESERVE_CLOSE`` over
+ // a `TALER_ReserveCloseRequestSignaturePS`.
+ 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 (with a status code of 400).
+ request_timestamp: Timestamp;
+
+ // payto://-URI of the account the reserve balance is to be
+ // wired to. Must be of the form: 'payto://$METHOD' for a
+ // wire method supported by this exchange (if the
+ // method is not supported, this is a bad request (400)).
+ // If not given, the reserve's origin account
+ // will be used. If no origin account is known for the
+ // reserve and not given, this is a conflict (409).
+ payto_uri?: string;
+
+ }
+
+ .. ts:def:: ReserveCloseResponse
+
+ interface ReserveCloseResponse {
+
+ // Actual amount that will be wired (excludes closing fee).
+ 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
new file mode 100644
index 00000000..34d27ded
--- /dev/null
+++ b/core/api-mailbox.rst
@@ -0,0 +1,215 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2022 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
+
+
+=======================
+The Mailbox RESTful API
+=======================
+
+This is a proposed API for the GNU Taler Mailbox service which allows Taler
+wallets to securely send push and pull payment requests to other wallets
+without having to interact with the respective messaging service.
+
+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:
+
+.. include:: tos.rst
+
+-------------------------
+Configuration information
+-------------------------
+
+.. http:get:: /config
+
+ Return the protocol version and currency supported by this service.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body is a `VersionResponse`.
+
+ **Details:**
+
+ .. ts:def:: VersionResponse
+
+ interface VersionResponse {
+ // libtool-style representation of the Merchant 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-mailbox";
+
+ // Fee per message.
+ message_fee: Amount;
+
+ // How long will the service store a message
+ // before giving up on delivery?
+ delivery_period: RelativeTime;
+
+ }
+
+----------------
+Sending messages
+----------------
+
+.. http:post:: /$H_MAILBOX
+
+ Puts a message into ``$H_MAILBOX``.
+ ``$H_MAILBOX`` is the SHA512 of an EdDSA public key.
+
+ **Request**
+
+ The body of the request must be an `IdentityMessage`.
+
+ **Response**
+
+ :http:statuscode:`204 No Content`
+ Message was stored and will be delivered.
+ :http:statuscode:`402 Payment Required`
+ Client needs to make a Taler payment before proceeding. See
+ standard Taler payment procedure.
+ :http:statuscode:`403 Forbidden`
+ The specified ``order_id`` does not permit sending
+ of this message. Possible reaons include the order
+ being for a different message, unpaid or
+ malformed.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`429 Too Many Requests`:
+ The system is currently experiencing a too high request
+ load and is unable to accept the message for delivery.
+ The response format is given by `MailboxRateLimitedResponse`_.
+
+ **Details:**
+
+ .. _IdentityMessage:
+ .. ts:def:: IdentityMessage
+
+ interface IdentityMessage {
+ // Public DH key used to encrypt the body. Must be fresh
+ // and only used once (ephemeral).
+ ephemeral_key: EcdhePublicKey;
+
+ // Encrypted message. Must be exactly 256-32 bytes long.
+ body: string;
+
+ // Order ID, if the client recently paid for this message.
+ order_id?: string;
+ }
+
+ .. _MailboxRateLimitedResponse:
+ .. ts:def:: MailboxRateLimitedResponse
+
+ interface MailboxRateLimitedResponse {
+
+ // Taler error code, TALER_EC_MAILBOX_DELIVERY_RATE_LIMITED.
+ code: number;
+
+ // When the client should retry.
+ retry_delay: RelativeTime;
+
+ // The human readable error message.
+ hint: string;
+
+ }
+
+------------------
+Receiving messages
+------------------
+
+.. http:get:: /$H_MAILBOX
+
+ Endpoint that returns unread messages in ``$H_MAILBOX``.
+ The number of messages returned by the service can be limited.
+ If the request is simply repeated, the same messages will be
+ returned again (or possibly more if additional messages arrived
+ and the total number is below the service's current internal limit).
+ To receive additional messages, the client generally has to first
+ explicitly DELETE already downloaded messages from the mailbox.
+
+ **Request:**
+
+ :query timeout_ms=NUMBER: *Optional.* If specified,
+ the Mailbox service will wait up to ``NUMBER``
+ milliseconds for the arrival of new messages
+ before sending the HTTP response. Note that if the
+ mailbox is non-empty, the service will always return
+ immediately with the messages in the mailbox, and not
+ wait for additional messages to arrive.
+
+ **Response**
+
+ :http:statuscode:`200 Ok`:
+ Messages are returned in binary format, 256 bytes per message,
+ starting with the ephemeral key and followed by
+ the encrypted body. Messages are not encapsulated in JSON!
+ :http:statuscode:`204 No Content`:
+ The mailbox is empty.
+ :http:statuscode:`429 Too Many Requests`:
+ The system is currently experiencing a too high request
+ load and is unable to accept the message for delivery.
+ The response format is given by `MailboxRateLimitedResponse`_.
+
+.. http:delete:: /$ADDRESS
+
+ Requests the deletion of already received messages from the
+ mailbox. Here, ``$ADDRESS`` must be the EdDSA public key
+ of the mailbox (not the hash!).
+
+ **Request**
+
+ The body of the request must be a ``MessageDeletionRequest``.
+
+ **Details:**
+
+ .. _MessageDeletionRequest:
+ .. ts:def:: MessageDeletionRequest
+
+ interface MessageDeletionRequest {
+
+ // Number of messages to delete. (Starting from the beginning
+ // of the latest GET response).
+ count: Integer;
+
+ // SHA-512 hash over all messages to delete.
+ checksum: HashCode;
+
+ // Signature by the mailbox's private key affirming
+ // the deletion of the messages, of purpuse
+ // ``TALER_SIGNATURE_WALLET_MAILBOX_DELETE_MESSAGES``.
+ wallet_sig: EddsaSignature;
+
+ }
+
+ **Response**
+
+ :http:statuscode:`204 No Content`:
+ Deletion complete.
+ :http:statuscode:`403 Forbidden`:
+ The ``wallet_sig`` is invalid.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ The checksum does not match the messages currently at the
+ head of the mailbox, or ``count`` is larger
+ than the number of messages currently in the mailbox.
+ This response comes with a standard `ErrorDetail` response.
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index 801da3d9..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-2020 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
@@ -16,14 +16,16 @@
@author Marcello Stanisci
@author Florian Dold
@author Christian Grothoff
+ @author Priscilla Huang
.. _merchant-api:
-====================
-Merchant Backend API
-====================
+============================
+Merchant Backend RESTful API
+============================
.. contents:: Table of Contents
+ :local:
-----------------------
Base URLs and Instances
@@ -34,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
@@ -72,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
@@ -97,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
@@ -114,11 +123,12 @@ 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:**
:http:statuscode:`200 OK`:
- The exchange accepted all of the coins. The body is a `VersionResponse`.
+ The body is a `VersionResponse`.
.. ts:def:: VersionResponse
@@ -131,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;
}
@@ -145,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
@@ -235,28 +288,34 @@ 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).
: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.
- The response will include the ``coin_pub`` for which the payment failed,
- in addition to the response from the exchange to the ``/deposit`` request.
+ 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
+ (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`:
The offer has expired and is no longer available.
:http:statuscode:`412 Precondition failed`:
The given exchange is not acceptable for this merchant, as it is not in the
list of accepted exchanges and not audited by an approved auditor.
+ TODO: Status code may be changed to 409 in the future as 412 is technically wrong.
:http:statuscode:`502 Bad gateway`:
The merchant's interaction with the exchange failed in some way.
The client might want to try again later.
@@ -279,6 +338,10 @@ Making the payment
// key of the merchant instance.
sig: EddsaSignature;
+ // Text to be shown to the point-of-sale staff as a proof of
+ // payment.
+ pos_confirmation?: string;
+
}
.. ts:def:: PayRequest
@@ -287,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;
@@ -303,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;
@@ -313,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
@@ -346,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:**
@@ -445,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
@@ -459,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 {
@@ -471,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;
}
@@ -507,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.
@@ -707,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
@@ -741,108 +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;
-
- // (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;
}
@@ -851,7 +926,7 @@ Instance management
-------------------
Instances allow one merchant backend to be shared by multiple merchants.
-Every backend must have at least one instance, typcially the "default"
+Every backend must have at least one instance, typically the "default"
instance setup before it can be used to manage inventory or process payments.
@@ -882,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_.@-]+$``.
@@ -897,6 +965,20 @@ 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;
+
+ // Merchant public website.
+ website?: string;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
+
// Authentication settings for this instance
auth: InstanceAuthConfigurationMessage;
@@ -907,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
@@ -931,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.
@@ -958,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"
@@ -967,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
@@ -992,15 +1122,24 @@ 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;
+
+ // Merchant public website.
+ website?: string;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
+
// The merchant's physical address (to be put into contracts).
address: Location;
@@ -1008,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
@@ -1066,6 +1198,15 @@ 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;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
+
// Merchant instance this response is about ($INSTANCE).
id: string;
@@ -1099,13 +1240,22 @@ 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;
+
+ // Merchant public website.
+ website?: string;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
+
// Public key of the merchant/instance, in Crockford Base32 encoding.
merchant_pub: EddsaPublicKey;
@@ -1116,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
@@ -1146,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
------------------
@@ -1237,15 +1362,25 @@ KYC status checks
request should be repeated.
The response will be an `AccountKycRedirects` object.
:http:statuscode:`204 No content`:
- All KYC operations queried have succeeded.
+ All KYC operations queried have succeeded. This may change in the
+ future, but there is no need to check again soon. It is suggested
+ to check again at a frequency of hours or days.
:http:statuscode:`502 Bad gateway`:
We failed to obtain a response from an exchange (about the KYC status).
The response will be an `AccountKycRedirects` object.
+ :http:statuscode:`503 Service unavailable`:
+ We do not know our KYC status as the exchange has not yet
+ returned the necessary details. This is not an actual failure:
+ this is expected to happen if for example a deposit was not yet aggregated
+ by the exchange and thus the exchange has not yet initiated
+ the KYC process. The client should simply try again later. It is
+ suggested to check again at a frequency of minutes to hours.
:http:statuscode:`504 Gateway Timeout`:
The merchant did not receive a confirmation from an exchange
within the specified time period. Used when long-polling for the
result.
- The response will be an `AccountKycRedirects` object.
+
+ **Details:**
.. ts:def:: AccountKycRedirects
@@ -1265,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;
@@ -1293,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
--------------------
@@ -1453,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`:
@@ -1476,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;
}
@@ -1536,7 +1870,7 @@ Inspecting inventory
next_restock?: Timestamp;
// Minimum age buyer must have (in years).
- minimum_age: Integer;
+ minimum_age?: Integer;
}
@@ -1662,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
@@ -1691,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
@@ -1705,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
@@ -1713,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;
+
+ // Human-readable description of the whole purchase.
+ summary: string;
- 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.
+ // Map from IETF BCP 47 language tags to localized summaries.
+ summary_i18n?: { [lang_tag: string]: string };
- .. ts:def:: MinimalOrderDetail
+ // 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;
- interface MinimalOrderDetail {
- // Amount to be paid by the customer.
- amount: Amount;
+ // List of contract choices that the customer can select from.
+ // @since protocol **vSUBSCRIBE**
+ choices?: ContractChoice[];
- // Short summary of the order.
- summary: string;
+ // 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
@@ -1811,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_ms`` are returned. Defaults to ``-20`` to return the last 20 entries (before ``start`` and/or ``date_ms``).
- :query date_ms: *Optional.* Non-negative date in milliseconds 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 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. 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:**
@@ -1871,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:**
@@ -1935,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
@@ -2209,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:**
@@ -2257,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
-----------------------------
@@ -2520,413 +2759,1001 @@ once we got a reply from the exchange.
The transfer cannot be deleted anymore.
---------------------
-Backend: Giving tips
---------------------
+-----------
+OTP Devices
+-----------
-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.
+OTP devices can be used to allow offline merchants
+to validate that a customer made a payment.
-Create reserve
---------------
+.. http:post:: [/instances/$INSTANCE]/private/otp-devices
-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.
+ This is used to associate an OTP device with an instance.
-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.
+ **Request:**
-.. _tips:
-.. http:post:: [/instances/$INSTANCE]/private/reserves
+ The request must be a `OtpDeviceAddDetails`.
- Create a reserve for tipping.
+ **Response:**
+
+ :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.
+
+ .. ts:def:: OtpDeviceAddDetails
+
+ interface OtpDeviceAddDetails {
+
+ // Device ID to use.
+ otp_device_id: 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;
+
+ // 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;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
- 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.
+
+.. http:patch:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
+
+ 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:**
- The request body is a `ReserveCreateRequest` object.
+ The request must be a `OtpDevicePatchDetails`.
**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:`204 No content`:
+ The template has successfully modified.
+ :http:statuscode:`404 Not found`:
+ The template(ID) is unknown to the backend.
: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.
+ The provided information is inconsistent with the current state of the template. Changes made is the same with
+ another store.
- .. ts:def:: ReserveCreateRequest
+ .. ts:def:: OtpDevicePatchDetails
- interface ReserveCreateRequest {
- // Amount that the merchant promises to put into the reserve.
- initial_balance: Amount;
+ interface OtpDevicePatchDetails {
- // Exchange the merchant intends to use for tipping.
- exchange_url: 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;
+
+ // Algorithm for computing the POS confirmation.
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
+
+
+.. http:get:: [/instances/$INSTANCE]/private/otp-devices
+
+ This is used to return the list of all the OTP devices.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned all the templates. Returns a `OtpDeviceSummaryResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: OtpDeviceSummaryResponse
- // Desired wire method, for example "iban" or "x-taler-bank".
- wire_method: string;
+ interface OtpDeviceSummaryResponse {
+
+ // Array of devices that are present in our backend.
+ otp_devices: OtpDeviceEntry[];
}
- .. ts:def:: ReserveCreateConfirmation
+ .. ts:def:: OtpDeviceEntry
- interface ReserveCreateConfirmation {
- // Public key identifying the reserve.
- reserve_pub: EddsaPublicKey;
+ interface OtpDeviceEntry {
- // Wire account of the exchange where to transfer the funds.
- payto_uri: string;
+ // Device identifier.
+ otp_device_id: string;
+
+ // Human-readable description for the device.
+ device_description: string;
}
-.. http:get:: [/instances/$INSTANCE]/private/reserves
+.. http:get:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
- Obtain list of reserves that have been created for tipping.
+ This is used to obtain detailed information about a specific OTP device.
- **Request:**
+ 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.
+
+ **Query:**
- :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.
+ :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`:
- Returns a list of known tipping reserves.
- The body is a `TippingReserveStatus`.
+ The backend has successfully returned the detailed information about a specific OTP device.
+ Returns a `OtpDeviceDetails`.
+ :http:statuscode:`404 Not found`:
+ The OTP device or instance is unknown to the backend.
- .. ts:def:: TippingReserveStatus
+ .. ts:def:: OtpDeviceDetails
- interface TippingReserveStatus {
- // Array of all known reserves (possibly empty!).
- reserves: ReserveStatusEntry[];
- }
+ interface OtpDeviceDetails {
- .. ts:def:: ReserveStatusEntry
+ // Human-readable description for the device.
+ device_description: string;
- interface ReserveStatusEntry {
- // Public key of the reserve.
- reserve_pub: EddsaPublicKey;
+ // 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;
- // Timestamp when it was established.
- creation_time: Timestamp;
+ // 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;
+
+ }
+
+.. http:delete:: [/instances/$INSTANCE]/private/otp-devices/$DEVICE_ID
- // Timestamp when it expires.
- expiration_time: Timestamp;
+ **Response:**
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the OTP device.
+ :http:statuscode:`404 Not found`:
+ The backend does not know the instance or the OTP device.
+
+
+---------
+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
+connectivity or where a Web site wants to enable payments on a purely static
+Web page (for example to collect donations). In these cases, the GNU Taler
+wallet can be used to setup an order (and then of course pay for it).
+
+The template itself primarily provides order details that cannot be be changed
+by the customer when the wallet creates the order. The idea is that the user
+*may* be prompted to enter certain information, such as the amount to be paid,
+or the order summary (as a reminder to themselves or a message to the store),
+while the template provides all of the other contract details.
+
+The typical user-experience with templatates is that the user first scans a QR
+code or clicks on a taler://-URI which contains a ``pay-template`` (see `LSD
+0006 <https://lsd.gnunet.org/lsd0006/>`__). The URI specifies which values the
+user should supply, currently either nothing, the amount, the order summary or
+both. The URI may also specify defaults or partial defaults for those
+values. After the user has supplied those values, the wallet will use the
+public template API to create the order, then fetch the order details, and
+proceed as if it had been given the respective ``pay`` URI in the first place:
+show the full contract details and allow the user to make a payment. If the
+user chooses to aborts the payment, the wallet should give the user the
+opportunity to edit the values and create another order with different values.
+If the template does not include any values that the user is allowed to edit
+(so it is basically a fixed contract), the wallet should directly create the
+order and immediately proceed to the contract acceptance dialog.
+
+The business process for the templating API is also pretty simple. First, the
+private API is used to setup (or edit) the template, providing all of the
+contract terms that subsequently cannot be changed by the customer/wallet.
+This template data is then stored under the template ID which can be freely
+chosen. The SPA should also make it easy for the merchant to convert the
+template ID into a taler://-URI and/or QR code. Here, the merchant must
+additionally specify the defaults (if any) for the customer-editable values.
+Afterwards, the merchant can print out the QR code for display at the store,
+add a link to the taler://-URI and/or embed the respective QR-code image into
+their Web page.
+
+To receive a payment confirmation, the mechant may choose to configure a
+webhook in the merchant backend on the ``pay`` action, for example to send an
+SMS to their mobile phone. For points-of-sale without a mobile phone or
+Internet connectivity, the TBD mechanism can also be used to confirm payments.
+
+
+
+Adding templates
+----------------
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
+.. http:post:: [/instances/$INSTANCE]/private/templates
- // Amount picked up so far.
- pickup_amount: Amount;
+ This is used to create a template.
- // Amount approved for tips that exceeds the pickup_amount.
- committed_amount: Amount;
+ **Request:**
- // Is this reserve active (false if it was deleted but not purged)?
- active: boolean;
+ The request must be a `TemplateAddDetails`.
+
+
+ **Response:**
+
+ :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.
+
+
+ .. ts:def:: TemplateAddDetails
+
+ interface TemplateAddDetails {
+
+ // Template ID to use.
+ template_id: string;
+
+ // Human-readable description for the template.
+ template_description: string;
+
+ // OTP device ID.
+ // This parameter is optional.
+ otp_id?: string;
+
+ // 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;
}
-Query funds remaining
----------------------
+ .. ts:def:: TemplateContractDetails
+
+ interface TemplateContractDetails {
+
+ // Human-readable summary for the template.
+ summary?: string;
-.. http:get:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB
+ // 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;
- Obtain information about a specific reserve that have been created for tipping.
+ // The price is imposed by the merchant and cannot be changed by the customer.
+ // This parameter is optional.
+ amount?: Amount;
+
+ // Minimum age buyer must have (in years). Default is 0.
+ minimum_age: Integer;
+
+ // The time the customer need to pay before his order will be deleted.
+ // It is deleted if the customer did not pay and if the duration is over.
+ pay_duration: RelativeTime;
+
+ }
+
+
+
+Editing templates
+-----------------
+
+
+.. http:patch:: [/instances/$INSTANCE]/private/templates/$TEMPLATE_ID
+
+ This is used to update a template. It is useful when we need to change information in the template 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 `TemplatePatchDetails`.
**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.
+ 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.
- .. ts:def:: ReserveDetail
- interface ReserveDetail {
- // Timestamp when it was established.
- creation_time: Timestamp;
+ .. ts:def:: TemplatePatchDetails
- // Timestamp when it expires.
- expiration_time: Timestamp;
+ interface TemplatePatchDetails {
- // Initial amount as per reserve creation call.
- merchant_initial_amount: Amount;
+ // Human-readable description for the template.
+ template_description: string;
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: Amount;
+ // OTP device ID.
+ // This parameter is optional.
+ otp_id?: string;
- // Amount picked up so far.
- pickup_amount: Amount;
+ // Additional information in a separate template.
+ template_contract: TemplateContractDetails;
- // Amount approved for tips that exceeds the pickup_amount.
- committed_amount: Amount;
+ // 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;
- // Array of all tips created by this reserves (possibly empty!).
- // Only present if asked for explicitly.
- tips?: TipStatusEntry[];
+ // 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;
+ }
- // 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;
- }
+Inspecting template
+-------------------
- .. ts:def:: TipStatusEntry
+.. http:get:: [/instances/$INSTANCE]/private/templates
- interface TipStatusEntry {
+ This is used to return the list of all the templates.
- // Unique identifier for the tip.
- tip_id: HashCode;
- // Total amount of the tip that can be withdrawn.
- total_amount: Amount;
+ **Response:**
- // Human-readable reason for why the tip was granted.
- reason: string;
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned all the templates. Returns a `TemplateSummaryResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: TemplateSummaryResponse
+
+ interface TemplateSummaryResponse {
+
+ // List of templates that are present in our backend.
+ templates: TemplateEntry[];
}
+ The `TemplateEntry` object describes a template. It has the following structure:
-Authorizing tips
-----------------
+ .. ts:def:: TemplateEntry
-.. http:post:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB/authorize-tip
+ interface TemplateEntry {
- Authorize creation of a tip from the given reserve.
+ // Template identifier, as found in the template.
+ template_id: string;
- **Request:**
+ // Human-readable description for the template.
+ template_description: string;
+
+ }
+
+.. http:get:: [/instances/$INSTANCE]/private/templates/$TEMPLATE_ID
+
+ This is used to obtain detailed information about a specific template.
- The request body is a `TipCreateRequest` object.
**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 template.
+ Returns a `TemplateDetails`.
: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.
+ The instance or template(ID) is unknown to the backend.
- .. ts:def:: TipCreateRequest
- interface TipCreateRequest {
- // Amount that the customer should be tipped.
- amount: Amount;
+ .. ts:def:: TemplateDetails
+
+ interface TemplateDetails {
+
+ // Human-readable description for the template.
+ template_description: string;
+
+ // OTP device ID.
+ // This parameter is optional.
+ otp_id?: string;
+
+ // Additional information in a separate template.
+ template_contract: TemplateContractDetails;
- // Justification for giving the tip.
- justification: string;
+ // 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;
- // URL that the user should be directed to after tipping,
- // will be included in the tip_token.
- next_url: string;
+ // 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:: TipCreateConfirmation
- interface TipCreateConfirmation {
- // Unique tip identifier for the tip that was created.
- tip_id: HashCode;
- // taler://tip URI for the tip.
- taler_tip_uri: string;
+Removing template
+-----------------
+
+.. http:delete:: [/instances/$INSTANCE]/private/templates/$TEMPLATE_ID
+
+ This is used to delete information about a template. If we no longer use it.
- // URL that will directly trigger processing
- // the tip when the browser is redirected to it.
- tip_status_url: string;
+ **Response:**
- // When does the tip expire?
- tip_expiration: Timestamp;
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the template.
+ :http:statuscode:`404 Not found`:
+ The backend does not know the instance or the 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/$INSTANCE]/private/tips
+.. http:post:: [/instances/$INSTANCES]/templates/$TEMPLATE_ID
+
+ This using template can be modified by everyone and will be used to create order.
- 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.
**Request:**
- The request body is a `TipCreateRequest` object.
+ The request must be a `UsingTemplateDetails` and we accept JSON application and URL encoded.
+
**Response:**
- :http:statuscode:`200 OK`:
- A tip has been created. The backend responds with a `TipCreateConfirmation`.
+ The response is exactly the same type of response as when
+ creating an order using :ref:`POST /private/orders <post-order>`.
+
+ **Details:**
+
+ .. ts:def:: UsingTemplateDetails
+
+ interface UsingTemplateDetails {
+
+ // Summary of the template
+ summary?: string;
+
+ // The amount entered by the customer.
+ amount?: Amount;
+ }
+
+
+--------
+Webhooks
+--------
+
+The webhook is a backend feature that is used to send a confirmation to the merchant. It can be send with a SMS, email or with another method. It will confirm that the
+customer paid the merchant. It will show the date and the price the customer paid.
+
+
+
+Adding webhooks
+---------------
+
+.. http:post:: [/instances/$INSTANCES]/private/webhooks
+
+ This is used to create a webhook.
+
+ **Request:**
+
+ The request must be a `WebhookAddDetails`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The creation of the webhook is successsful.
+
: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 merchant instance is unknown or it not in our data.
+ .. ts:def:: WebhookAddDetails
-Deleting reserves
------------------
+ interface WebhookAddDetails {
-.. http:delete:: [/instances/$INSTANCE]/private/reserves/$RESERVE_PUB
+ // Webhook ID to use.
+ webhook_id: 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.
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
+
+ // URL of the webhook where the customer will be redirected.
+ url: string;
+
+ // Method used by the webhook
+ http_method: string;
+
+ // Header template of the webhook
+ header_template?: string;
+
+ // Body template by the webhook
+ body_template?: string;
+
+ }
+
+
+Editing webhooks
+----------------
+
+.. http:patch:: [/instances/$INSTANCES]/private/webhooks/$WEBHOOK_ID
+
+ This is used to update a webhook.
**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.
+ The request must be a `WebhookPatchDetails`.
**Response:**
:http:statuscode:`204 No content`:
- The backend has successfully deleted the reserve.
+ The webhook has successfully modified.
:http:statuscode:`404 Not found`:
- The backend does not know the instance or the reserve.
+ The webhook(ID) is unknown to the backend.
:http:statuscode:`409 Conflict`:
- The backend refuses to delete the reserve (committed tips awaiting pickup).
+ The provided information is inconsistent with the current state of the webhook. Changes made is the same with another store.
+ .. ts:def:: WebhookPatchDetails
-Checking tip status
--------------------
+ interface WebhookPatchDetails {
-.. http:get:: [/instances/$INSTANCE]/private/tips/$TIP_ID
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
- Obtain information about a particular tip.
+ // URL of the webhook where the customer will be redirected.
+ url: string;
+
+ // Method used by the webhook
+ http_method: string;
+
+ // Header template of the webhook
+ header_template?: string;
+
+ // Body template by the webhook
+ body_template?: string;
+
+ }
- **Request:**
- :query pickups: If set to "yes", returns also information about all of the pickups.
+
+Inspecting webhook
+------------------
+
+.. http:get:: [/instances/$INSTANCES]/private/webhooks
+
+ This is used to return all the webhooks that are present in our backend.
**Response:**
:http:statuscode:`200 OK`:
- The tip is known. The backend responds with a `TipDetails` message.
+ The backend has successfully returned all the webhooks. Returns a `WebhookSummaryResponse`.
+
:http:statuscode:`404 Not found`:
- The tip is unknown to the backend.
+ The backend has does not know about the instance.
- .. ts:def:: TipDetails
+ .. ts:def:: WebhookSummaryResponse
- interface TipDetails {
- // Amount that we authorized for this tip.
- total_authorized: Amount;
+ interface WebhookSummaryResponse {
- // Amount that was picked up by the user already.
- total_picked_up: Amount;
+ // Return webhooks that are present in our backend.
+ webhooks: WebhookEntry[];
- // 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;
+ The `WebhookEntry` object describes a webhook. It has the following structure:
- // Reserve public key from which the tip is funded.
- reserve_pub: EddsaPublicKey;
+ .. ts:def:: WebhookEntry
- // Array showing the pickup operations of the wallet (possibly empty!).
- // Only present if asked for explicitly.
- pickups?: PickupDetail[];
- }
+ interface WebhookEntry {
+
+ // Webhook identifier, as found in the webhook.
+ webhook_id: string;
+
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
- .. ts:def:: PickupDetail
+ }
+
+
+.. http:get:: [/instances/$INSTANCES]/private/webhooks/$WEBHOOK_ID
+
+ This is used to obtain detailed information about apecific template.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the detailed information about a specific webhook. Returns a `WebhookDetails`.
+
+ :http:statuscode:`404 Not found`:
+ The webhook(ID) is unknown to the backend.
+
+
+ .. ts:def:: WebhookDetails
+
+ interface WebhookDetails {
+
+ // The event of the webhook: why the webhook is used.
+ event_type: string;
+
+ // URL of the webhook where the customer will be redirected.
+ url: string;
+
+ // Method used by the webhook
+ http_method: string;
+
+ // Header template of the webhook
+ header_template?: string;
+
+ // Body template by the webhook
+ body_template?: string;
+
+ }
+
+
+Removing webhook
+-----------------
+
+.. http:delete:: [/instances/$INSTANCES]/private/webhooks/$WEBHOOK_ID
- interface PickupDetail {
- // Unique identifier for the pickup operation.
- pickup_id: HashCode;
+ This is used to delete information about a webhook.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The backend has successfully deleted the webhook.
+
+ :http:statuscode:`404 Not found`:
+ The webhook(ID) or the instance is unknown to the backend.
+
+
+
+----------------------------------------
+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.
- // Number of planchets involved.
- num_planchets: Integer;
+ .. ts:def:: TokenFamilyCreateRequest
- // Total amount requested for this pickup_id.
- requested_amount: Amount;
+ 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",
}
-.. http:get:: [/instances/$INSTANCE]/private/tips
+Updating token families
+-----------------------
+
+.. http:patch:: [/instances/$INSTANCES]/private/tokenfamilies/$TOKEN_FAMILY_SLUG
- Return the list of all tips.
+ This is used to update a token family.
**Request:**
- :query include_expired: *Optional*. If set to "yes", the result includes expired tips also. Otherwise, only active tips are returned.
+ 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.
- :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.
+ .. ts:def:: TokenFamilyUpdateRequest
- :query offset: *Optional*. Starting ``row_id`` for an iteration.
+ 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 backend has successfully found the list of tips. The backend responds
- with a `TipsResponse`.
+ The merchant backend has successfully returned all token families.
+ Returns a `TokenFamiliesList`.
- .. ts:def:: TipsResponse
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the instance.
- interface TipsResponse {
+ .. ts:def:: TokenFamiliesList
- // List of tips that are present in the backend.
- tips: Tip[];
- }
+ // TODO: Add pagination
- .. ts:def:: Tip
+ interface TokenFamiliesList {
- interface Tip {
+ // All configured token families of this instance.
+ token_families: TokenFamilySummary[];
- // ID of the tip in the backend database.
- row_id: number;
+ }
- // Unique identifier for the tip.
- tip_id: HashCode;
+ .. ts:def:: TokenFamilySummary
- // (Remaining) amount of the tip (including fees).
- tip_amount: Amount;
- }
+ 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.
@@ -2959,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.
@@ -2971,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.
@@ -2989,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[];
@@ -3042,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[];
@@ -3081,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:
@@ -3139,6 +4058,15 @@ It has the following structure:
name: string;
// Label for a location with the business address of the merchant.
+ email?: string;
+
+ // Label for a location with the business address of the merchant.
+ website?: string;
+
+ // An optional base64-encoded product image.
+ logo?: ImageDataUrl;
+
+ // Label for a location with the business address of the merchant.
address?: Location;
// Label for a location that denotes the jurisdiction for disputes.
@@ -3203,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 759a4c3f..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,14 +102,20 @@ 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:
---------------------------
-Receiving Terms of Service
---------------------------
+
+.. include:: tos.rst
+
+-----------------------
+Receiving Configuration
+-----------------------
.. http:get:: /config
- Obtain the terms of service provided by the storage service.
+ Obtain the key configuration settings of the storage service.
+ This specification corresponds to ``current`` protocol being version **2**.
**Response:**
@@ -135,11 +141,18 @@ Receiving Terms of Service
// 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:
+----------------------
+Recovering Backup Data
+----------------------
+
.. http:get:: /backups/${ACCOUNT-KEY}
Download latest version of the backup.
@@ -200,6 +213,10 @@ Receiving Terms of Service
signature validation.
+---------------------
+Uploading Backup Data
+---------------------
+
.. http:post:: /backups/${ACCOUNT-KEY}
Upload a new version of the account's database, or download the
diff --git a/core/api-taldir.rst b/core/api-taldir.rst
new file mode 100644
index 00000000..4da9bb02
--- /dev/null
+++ b/core/api-taldir.rst
@@ -0,0 +1,258 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2022 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 Martin Schanzenbach
+
+
+======================
+The TalDir RESTful API
+======================
+
+This is a proposed API for the TalDir service which allows Taler wallets to
+securely associate an inbox service (URL and public key) with the address of a
+messaging service used by the wallet's user. Wallets can also lookup the
+inbox of other users. This will enable wallets to make wallet-to-wallet
+payments to distant wallets where the target user is only identified by their
+address in a messaging service. Examples for messaging services include E-mail
+and SMS.
+
+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:
+
+.. include:: tos.rst
+
+
+-------------------------
+Configuration information
+-------------------------
+
+.. http:get:: /config
+
+ Return the protocol version and currency supported by this service.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The body is a `VersionResponse`.
+
+ .. ts:def:: VersionResponse
+
+ interface VersionResponse {
+ // libtool-style representation of the Merchant 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-directory";
+
+ // Supported registration methods
+ methods: TaldirMethod[];
+
+ // fee for one month of registration
+ monthly_fee: Amount;
+
+ }
+
+ .. ts:def:: TaldirMethod
+
+ interface TaldirMethod {
+ // Name of the method, e.g. "email" or "sms".
+ name: string;
+
+ // per challenge fee
+ challenge_fee: Amount;
+
+ }
+
+--------------------
+Address registration
+--------------------
+
+.. http:post:: /register/$METHOD
+
+ Endpoint to register, extend or modify the registration for an address in
+ the directory.
+ Here, $METHOD is the type of address to register, e.g. "email", or "phone".
+ Supported methods are listed in the VersionResponse.
+ Note that duration should be given as a multiple of a month in microseconds.
+ If the duration is not a multiple of a month it will be rounded to the
+ nearest multiple. Halfway values will be rounded away from zero.
+ The cost calculation and resulting registration validity will be adjusted
+ automatically.
+ In order to only modify the data, the duration may be set to 0.
+ When the call is made with unmodified data and a duration of 0, the
+ endpoint will return how long this registration is currently paid for.
+
+ **Request**
+
+ .. ts:def:: IdentityMessage
+
+ interface IdentityMessage {
+ // Address, in $METHOD-specific format
+ address: string;
+
+ // Public key of the user to register. As string in Crockfor base32 encoding.
+ public_key: EddsaPublicKey;
+
+ // (HTTPS) endpoint URL for the inbox service.
+ inbox_url: string;
+
+ // For how long should the registration last/be extended.
+ duration: RelativeTime;
+
+ }
+
+ **Response**
+
+ :http:statuscode:`200 Ok`
+ Registration already exists for this address for the specified duration.
+ Returns for how long this registration is paid for.
+ The response format is given by `AlreadyPaidResponse`_.
+ :http:statuscode:`202 Accepted`
+ Registration was initiated, the client should check for receiving
+ a challenge at the address where registration was attempted.
+ :http:statuscode:`402 Payment Required`
+ Client needs to make a Taler payment before proceeding. See
+ standard Taler payment procedure.
+ :http:statuscode:`404 Not found`
+ The TalDir service does not support the specified method.
+ This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`429 Too Many Requests`:
+ The client exceeded the number of allowed attempts for initiating
+ a challenge for this address in the given timeframe.
+ The response format is given by `RateLimitedResponse`_.
+
+ .. _RateLimitedResponse:
+ .. ts:def:: RateLimitedResponse
+
+ interface RateLimitedResponse {
+
+ // Taler error code, TALER_EC_TALDIR_REGISTER_RATE_LIMITED.
+ code: number;
+
+ // At what frequency are new registrations allowed.
+ request_frequency: RelativeTime;
+
+ // The human readable error message.
+ hint: string;
+
+ }
+
+ .. _AlreadyPaidResponse:
+ .. ts:def:: AlreadyPaidResponse
+
+ interface AlreadyPaidResponse {
+
+ // The remaining duration for which this registration is still paid for
+ valid_for: RelativeTime;
+
+ }
+
+
+.. http:get:: /register/$H_ADDRESS/$PINTAN
+
+ Endpoint that generates an HTML Web site with a QR code and
+ ``taler://taldir/$H_ADDRESS/$PINTAN-wallet`` link for completing the
+ registration. Useful to open the registration challenge in a browser (say if
+ it was received on a different device than where the wallet is running).
+ Does NOT complete the registration, as some providers automatically click on
+ all links in messages. Yes, we do not like them doing so either, but ``GET``
+ is a "safe" method according to the HTTP standard, so technically this is
+ allowed.
+
+ Opening the link will lead the **wallet** to do the POST call below. If the
+ Taler wallet can somehow intercept the URL (say for SMS, if it has the right
+ permissions) it can skip this request and directly do the POST, as all of
+ the required new information is already encoded in the URL.
+
+ Note that the wallet must be involved before the POST is made, as the
+ wallet's public key from the registration must be hashed with the ``$PINTAN``
+ to protect the user against phishing. Otherwise, someone else might attempt
+ a concurrent registration of a different public key, and the user might
+ accidentally authorize the registration of the public key of a different
+ wallet.
+
+.. http:post:: /$H_ADDRESS
+
+ This request is the last step of a registration, proving to the TalDir that
+ the user of the wallet is indeed able to receive messages at the specified
+ address. ``$H_ADDRESS`` is the SHA-512 hash of the address to be registered in
+ Crockford base32 encoding.
+
+ **Request**
+
+ .. ts:def:: IdentityConfirmation
+
+ interface IdentityConfirmation {
+ // The solution is the SHA-512 hash of the challenge ($PINTAN) value
+ // chosen by TalDir (encoded as string just as given in the URL, but
+ // excluding the 0-termination) concatenated with the binary 32-byte
+ // value representing the wallet's EdDSA public key.
+ // The hash is provided as string in Crockford base32 encoding.
+ solution: HashCode;
+
+ }
+
+ **Response**
+
+ :http:statuscode:`204 No Content`:
+ Registration complete.
+ :http:statuscode:`403 Forbidden`:
+ The ``solution`` is invalid. Retrying immediately is allowed.
+ :http:statuscode:`404 Not found`:
+ The address is unknown (original registration attempt may have expired).
+ :http:statuscode:`429 Too Many Requests`:
+ The client exceeded the number of allowed attempts for solving
+ a challenge for this address in the given timeframe.
+
+--------------
+Address lookup
+--------------
+
+.. http:get:: /$H_ADDRESS
+
+ Lookup the public key (and mailbox service base URL) associated with
+ an address in the TalDir. Here, ``$H_ADDRESS`` is the SHA-512 hash of
+ a (presumably) registered address in Crockford base32 encoding.
+
+ **Response**
+
+ Standard HTTP cache control headers are used to specify how long the
+ registration is still expected to be valid.
+
+ :http:statuscode:`200 Ok`:
+ Registration information returned, of type `MailboxDetailResponse`
+ :http:statuscode:`404 Not found`:
+ The address is unknown (original registration may have expired).
+
+ .. _MailboxDetailResponse:
+ .. ts:def:: MailboxDetailResponse
+
+ interface MailboxDetailResponse {
+
+ // Registered public key of the user. As string in Crockford base32 encoding.
+ public_key: EddsaPublicKey;
+
+ // (HTTPS) endpoint URL for the inbox service.
+ inbox_url: string;
+
+ }
diff --git a/core/errors.rst b/core/errors.rst
deleted file mode 100644
index f7b490da..00000000
--- a/core/errors.rst
+++ /dev/null
@@ -1,79 +0,0 @@
-Errors and Testing
-##################
-
-This page lists errors that occur during the operation of GNU Taler.
-
-
-Error Conditions
-================
-
-``EXCHANGE_KEYS_INFO_UNAVAILABLE``
- An exchange does not return an HTTP 200 response for the ``/keys`` request.
-
-``EXCHANGE_KEYS_INFO_MALFORMED``
- The exchange returned an HTTP 200 response, but the body
- did not confirm the schema for ``/keys``.
-
-``EXCHANGE_KEYS_INFO_OUTDATED``
- The exchange returned a response for ``/keys`` with an issuing date
- earlier than the previous one.
-
- **Type**: Warning.
- **Handling**: The wallet should ignore the response and try again later.
-
-``EXCHANGE_WIRE_INFO_UNAVAILABLE``
- An exchange does not return an HTTP 200 response for the ``/wire`` request.
-
-``EXCHANGE_WIRE_INFO_MALFORMED``
- The exchange returned an HTTP 200 response, but the body
- did not confirm the schema for ``/wire``.
-
-``EXCHANGE_PROTOCOL_VERSION_UNSUPPORTED``
- An exchange's ``/keys`` response indicates a version number that
- is not compatible with the client.
-
-``EXCHANGE_MASTER_PUB_CHANGED``
- An exchange returns a ``/keys`` response with a master public key that differs
- from a previous response.
-
-``EXCHANGE_DENOM_MISSING``
- A denomination that has been previously offered by the exchange is not offered anymore,
- even though it hasn't expired yet.
-
-``EXCHANGE_DENOM_SIGNATURE_INVALID``
- The signature by the exchange's master key on a denomination is invalid.
-
-``EXCHANGE_DENOM_CHANGED``
- A denomination offered by the exchange is valid (syntax, content, signature),
- but has different information (fees, expiry) for the same public key compared
- to a previous keys response.
-
-``EXCHANGE_DENOM_CONTENT_INVALID``
- A denomination offered by the exchange is syntactically correct, but
- semantically malformed. For example, the expiration dates are not in the
- correct temporal order or the denomination public key can't be decoded.
-
-``EXCHANGE_WIRE_FEE_SIGNATURE_INVALID``
- The signature by the exchange's master key on a wire fee is invalid.
-
-``EXCHANGE_DENOMS_INADEQUATE``
- The denominations currently offered are inadequate for withdrawing digital cash.
- This can happen when all offered denominations are past their withdrawal expiry date.
-
-``EXCHANGE_RESERVE_STATUS_UNAVAILABLE``
-
-``WALLET_BUG``
- The wallet encountered a programming bug that should be reported to the developers.
-
- **Handling**: The wallet should allow the user to report this bug to the wallet developers.
-
-
-End-To-End Testing Scenarios
-============================
-
-This section describes some advanced end-to-end testing scenarios that should
-eventually be covered by our tests.
-
-* Reserve is created, closed, and then money is sent again to the reserve.
-
-* Amount from recoup should end up in customer's account again.
diff --git a/core/index-bank-apis.rst b/core/index-bank-apis.rst
new file mode 100644
index 00000000..f108df32
--- /dev/null
+++ b/core/index-bank-apis.rst
@@ -0,0 +1,38 @@
+..
+ 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
+
+=================
+Bank RESTful APIs
+=================
+
+
+.. toctree::
+ :maxdepth: 1
+
+ intro-bank-apis
+ api-corebank
+ api-bank-wire
+ api-bank-revenue
+ api-bank-integration
+ api-bank-conversion-info
+
+.. toctree::
+ :hidden:
diff --git a/core/index.rst b/core/index.rst
index 6dd762fd..8a764c10 100644
--- a/core/index.rst
+++ b/core/index.rst
@@ -1,6 +1,6 @@
..
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
@@ -23,22 +23,29 @@
Core Protocol Specification
---------------------------
-The *Protocol Specification* defines the HTTP-based, predominantly RESTful
-interfaces between the core components of Taler.
+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 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-wire
- api-bank-merchant
- api-bank-integration
- api-bank-access
- wireformats
- taler-uri
- errors
+ api-challenger
+ api-taldir
+ api-mailbox
+ index-bank-apis
+ api-donau
+
+.. toctree::
+ :hidden:
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/taler-uri.rst b/core/taler-uri.rst
deleted file mode 100644
index dd45611e..00000000
--- a/core/taler-uri.rst
+++ /dev/null
@@ -1,149 +0,0 @@
-.. _taler-uri-scheme:
-
-=======================
-The taler:// URI scheme
-=======================
-
-The ``taler`` URI scheme represents actions that are processed by a Taler
-wallet. This document uses `RFC 6570 URI templates
-<https://tools.ietf.org/html/rfc6570>`__ to describe the syntax.
-
-The basic syntax is as follows:
-
-.. code:: none
-
- taler://{action}/{+rest}
-
-The alternative scheme name ``taler+http`` indicates that the referenced resource is to be accessed
-via plain HTTP instead of HTTPS. This **should** only be used for testing. A production
-version of the wallet **may** reject such URIs.
-
---------------------
-Requesting a Payment
---------------------
-
-Payments are requested with the ``pay`` action. The parameters are a hierarchical identifier for the requested payment:
-
-.. code:: none
-
- taler://pay/{merchant_host}{/merchant_prefix_path*}/{order_id}/{session_id}{?c}{#ssid}
-
-* ``merchant_host`` is the hostname (and optionally port) of the merchant.
-* ``merchant_prefix_path`` is an optional list of path components that identifies the path prefix of the merchant base URL.
-* ``order_id`` is the order ID that the customer is asked to pay for.
-* ``session_id`` is the optional session ID under which the payment takes place.
-* ``c`` is the optional high-entropy order `ClaimToken`.
-* ``ssid`` is the optional WLAN SSID that the merchant can offer the wallet to connect to for Internet connectivity.
-
-Examples:
-
-.. code:: none
-
- # Example 1:
- # Order ID "ORD123", no session ID.
- # Merchant backend base URL https://backend.test.taler.net/
- taler://pay/test.taler.net/ORD123/
-
- # Example 2:
- # Order ID "ORD123", no session ID.
- # Merchant backend base URL https://backend.test.taler.net/instances/x1/
- taler://pay/test.taler.net/instances/x1/ORD123/
-
- # Example 3:
- # Order ID "ORD123", session ID "ABC123".
- # Merchant backend base URL https://backend.test.taler.net/
- taler://pay/test.taler.net/ORD123/ABC123
-
- # INVALID Example 1:
- # When no session ID is given, the slash must still be present.
- taler://pay/test.taler.net/ORD123
-
-------------------------
-Withdrawing (Initiation)
-------------------------
-
-The action ``withdraw`` is used to trigger a bank-integrated withdrawal operation.
-
-.. code:: none
-
- taler://withdraw/{bank_host}{/bank_prefix_path*}/{withdrawal_uid}{#ssid}
-
-* ``bank_host`` is the hostname of the merchant.
-* ``bank_prefix_path`` is an optional list of path components that identifies the path prefix of the bank integration API base URL.
-* ``withdrawal_uid`` is the unique ID of the withdrawal operation.
-* ``ssid`` is the optional WLAN SSID that the bank (typically in an ATM scenario) can offer the wallet
- to connect to for Internet connectivity.
-
---------------------------
-Withdrawing (Confirmation)
---------------------------
-
-.. code:: none
-
- taler://notify-reserve/{reserve_pub}
-
-This action notifies the wallet that the status of a reserve has changed. It is used
-by the bank to indicate that the withdrawal has been confirmed by the user (e.g. via 2FA / mTAN / ...).
-The wallet then re-checks the status of all unconfirmed reserves.
-
-Optionally, ``reserve_pub`` can be specified to also indicate the reserve that
-has been updated.
-
-
----------
-Refunding
----------
-
-A ``taler://refund`` URI instructs the wallet to download and apply available refunds for an already paid order.
-
-.. code:: none
-
- taler://refund/{merchant_host}{/merchant_prefix_path*}/{order_id}/{#ssid}
- taler+http://refund/{merchant_host}{/merchant_prefix_path*}/{order_id}/{#ssid}
-
-* ``merchant_host`` is the hostname of the merchant.
-* ``merchant_prefix_path`` is an optional list of path components that identifies the path prefix of the merchant base URL.
-* ``order_id`` is the order ID to check for refunds.
-* ``ssid`` is the optional WLAN SSID that the merchant can offer the wallet to connect to for Internet connectivity.
-
-
--------
-Tipping
--------
-
-A tipping URI instructs the wallet to download information about a tip from
-a merchant and ask the user to accept/decline it.
-
-.. code:: none
-
- taler://tip/{merchant_host}{/merchant_prefix_path*}/{tip_id}/{#ssid}
-
-* ``merchant_host`` is the hostname of the merchant.
-* ``merchant_prefix_path`` is an optional list of path components that identifies the path prefix of the merchant base URL.
-* ``tip_id`` uniquely identifies the tip.
-* ``insecure`` is an optional query parameter. When "1", the ``merchant_host`` is contacted via HTTP.
- When absent or "0", the ``merchant_host`` is contacted via HTTPS.
-* ``ssid`` is the optional WLAN SSID that the merchant can offer the wallet to connect to for Internet connectivity.
-
-
-----------------
-Adding exchanges
-----------------
-
-A ``taler://exchange/`` URI instructs the wallet to display a prompt to the user, asking
-the user to confirm/decline adding the exchange to the list of trusted exchanges.
-
-.. code:: none
-
- taler://exchange/{exchange_host}{/exchange_prefix_path*}/
-
----------------
-Adding auditors
----------------
-
-A ``taler://auditor/`` URI instructs the wallet to display a prompt to the user, asking
-the user to confirm/decline adding the auditor to the list of trusted auditors.
-
-.. code:: none
-
- taler://auditor/{auditor_host}{/auditor_prefix_path*}/
diff --git a/core/tos.rst b/core/tos.rst
new file mode 100644
index 00000000..6c36be4c
--- /dev/null
+++ b/core/tos.rst
@@ -0,0 +1,43 @@
+--------------------
+Terms of service API
+--------------------
+
+These APIs allow clients to obtain the terms of service
+and the privacy policy of a service.
+
+
+.. http:get:: /terms
+
+ Get the terms of service of the service.
+ The endpoint will consider the "Accept" and "Accept-Language" and
+ "Accept-Encoding" headers when generating a response. Specifically,
+ it will try to find a response with an acceptable mime-type, then
+ pick the version in the most preferred language of the user, and
+ finally apply compression if that is allowed by the client and
+ deemed beneficial.
+
+ The endpoint will set an "Etag", and subsequent requests of the same client
+ should provide the tag in an "If-None-Match" header to detect if the terms
+ of service have changed. If not, a "304 Not Modified" response will be
+ returned. Note that the "304 Not Modified" will also be returned if the
+ client changed the "Accept-Language" or "Accept-Encoding" header. Thus, if
+ the client would like to download the resource in a different language or
+ format, the "If-None-Match" header must be omitted.
+
+ If the "Etag" is missing, the client should not cache the response and
+ instead prompt the user again at the next opportunity. This is usually only
+ 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 "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.
+
+
+.. http:get:: /privacy
+
+ Get the privacy policy of the service. Behaves the same way as
+ The "/terms" endpoint, except that it returns the privacy policy
+ instead of the terms of service.
diff --git a/core/wireformats.rst b/core/wireformats.rst
deleted file mode 100644
index 591f1ce8..00000000
--- a/core/wireformats.rst
+++ /dev/null
@@ -1,62 +0,0 @@
-.. _wireformats:
-
-Wire Transfer Methods
-=====================
-
-A wire transfer is essential for the exchange to transfer funds into a merchant's
-account upon a successful deposit (see :ref:`deposit request <deposit>`). The
-merchant has to include the necessary information for the exchange to initiate the
-wire transfer.
-
-The information required for a wire transfer depends on the method of wire transfer
-used. Since the wire transfers differ for each region, we document here the
-ones currently supported by the exchange.
-
-X-TALER-BANK
-------------
-
-The ``x-taler-bank`` wire format is used for testing and for integration with Taler's
-simple "bank" system which in the future might be useful to set up a bank
-for a local / regional currency or accounting system. Using the ``x-taler-bank``
-wire method in combination with the Taler's bank, it is thus possible to
-fully test the Taler system without using "real" currencies. The URL
-format for ``x-taler-bank`` is simple, in that it only specifies an account
-number and the origin (domain and optionally a port) of the bank:
-
-.. code-block:: none
-
- payto://x-taler-bank/BANK_URI/ACCOUNT_IDENTIFIER
-
-The account identifier given must be a non-empty alphanumeric ASCII string. As with
-any ``payto://`` URI, additional fields may be present (after a ``?``), but
-are not required. The ``BANK_URI`` may include a port number. If none is
-given, ``https`` over port 443 is assumed. If a port number is
-given, ``http`` over the given port is to be used. Note that this
-means that you cannot run an ``x-taler-bank`` over ``https`` on a
-non-canonical port.
-
-Note that a particular exchange is usually only supporting one particular bank
-with the ``x-taler-bank`` wire format, so it is not possible for a merchant with
-an account at a different bank to use ``x-taler-bank`` to transfer funds across
-banks. After all, this is for testing and not for real banking.
-
-SEPA
-----
-
-The Single Euro Payments Area (SEPA) [#sepa]_ is a regulation for electronic
-payments. Since its adoption in 2012, all of the banks in the Eurozone and some
-banks in other countries adhere to this standard for sending and receiving
-payments. Note that the currency of the transfer will (currently) always be ``EUR``. In
-case the receiving account is in a currency other than ``EUR``, the receiving bank
-may convert the amount into that currency; currency exchange charges may be
-levied by the receiving bank.
-
-For the merchant to receive deposits through SEPA, the deposit request must
-follow the ``payto://`` specification for SEPA:
-
-.. code-block:: none
-
- payto://iban/IBAN
-
-.. [#sepa] SEPA - Single Euro Payments Area:
- http://www.ecb.europa.eu/paym/sepa/html/index.en.html