summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api-auditor.rst3170
-rw-r--r--core/api-bank-conversion-info.rst4
-rw-r--r--core/api-bank-integration.rst155
-rw-r--r--core/api-bank-wire.rst24
-rw-r--r--core/api-challenger.rst83
-rw-r--r--core/api-common.rst55
-rw-r--r--core/api-corebank.rst113
-rw-r--r--core/api-exchange.rst863
-rw-r--r--core/api-merchant.rst190
-rw-r--r--core/api-terminal.rst356
-rw-r--r--core/index-bank-apis.rst1
-rw-r--r--design-documents/023-taler-kyc.rst1479
-rw-r--r--design-documents/046-mumimo-contracts.rst2
-rw-r--r--design-documents/053-wallet-ui.rst39
-rw-r--r--design-documents/054-dynamic-form.rst673
-rw-r--r--frags/installing-ubuntu.rst6
-rw-r--r--images/regional-arch.pngbin117525 -> 0 bytes
-rw-r--r--index.rst2
-rw-r--r--libeufin/index.rst1
-rw-r--r--libeufin/regional-automated-manual.rst1
-rw-r--r--libeufin/regional-custom-manual.rst1
-rw-r--r--libeufin/regional-manual-use.rst (renamed from frags/regional-manual-use.rst)19
-rw-r--r--manpages/libeufin-nexus.1.rst22
-rw-r--r--manpages/libeufin-nexus.conf.5.rst58
-rw-r--r--manpages/taler.conf.5.rst189
-rw-r--r--orphaned/python-guidelines.rst (renamed from python-guidelines.rst)0
-rw-r--r--orphaned/qa-0.9.4.rst (renamed from checklists/qa-0.9.4.rst)0
-rw-r--r--screenshots/cta-balance-list-android-1.pngbin0 -> 51463 bytes
l---------screenshots/cta-balance-list-android-latest.png2
-rw-r--r--screenshots/cta-payment-paid-android-1.pngbin0 -> 44605 bytes
l---------screenshots/cta-payment-paid-android-latest.png2
-rw-r--r--screenshots/cta-peer-pull-initiate-android-1.pngbin0 -> 43717 bytes
l---------screenshots/cta-peer-pull-initiate-android-latest.png2
-rw-r--r--screenshots/cta-transaction-list-android-1.pngbin0 -> 86414 bytes
l---------screenshots/cta-transaction-list-android-latest.png2
-rw-r--r--screenshots/cta-url-entry-android-1.pngbin0 -> 23514 bytes
l---------screenshots/cta-url-entry-android-latest.png2
-rw-r--r--screenshots/cta-wire-transfer-android-1.pngbin0 -> 116541 bytes
l---------screenshots/cta-wire-transfer-android-latest.png2
l---------screenshots/cta-withdraw-confirm-android-latest.png1
l---------screenshots/cta-withdraw-confirm-chrome-latest.png1
l---------screenshots/cta-withdraw-confirm-firefox-latest.png1
l---------screenshots/cta-withdraw-confirm-ios-latest.png1
-rw-r--r--screenshots/cta-withdraw-done-android-1.pngbin0 -> 33779 bytes
l---------screenshots/cta-withdraw-done-android-latest.png2
l---------screenshots/cta-withdraw-firefox-latest.png1
-rw-r--r--screenshots/dynamic-forms.amount.pngbin0 -> 9763 bytes
-rw-r--r--screenshots/dynamic-forms.boolean.pngbin0 -> 7492 bytes
-rw-r--r--screenshots/dynamic-forms.choice.pngbin0 -> 9528 bytes
-rw-r--r--screenshots/dynamic-forms.decorative-elements.pngbin0 -> 22289 bytes
-rw-r--r--screenshots/dynamic-forms.file.pngbin0 -> 51502 bytes
-rw-r--r--screenshots/dynamic-forms.list.pngbin0 -> 17007 bytes
-rw-r--r--screenshots/dynamic-forms.time.pngbin0 -> 10026 bytes
-rw-r--r--system-administration/images/grafana-postgres-exporter.png (renamed from images/grafana-postgres-exporter.png)bin244971 -> 244971 bytes
-rw-r--r--system-administration/images/kuma.png (renamed from images/kuma.png)bin244687 -> 244687 bytes
-rw-r--r--system-administration/images/lego-logo.svg1
-rw-r--r--system-administration/images/taler-monitoring-infrastructure.png (renamed from images/taler-monitoring-infrastructure.png)bin85006 -> 85006 bytes
-rw-r--r--system-administration/images/uptime-kuma-edit.png (renamed from images/uptime-kuma-edit.png)bin116550 -> 116550 bytes
-rw-r--r--system-administration/images/uptime-kuma-from-grafana.png (renamed from images/uptime-kuma-from-grafana.png)bin345702 -> 345702 bytes
-rw-r--r--system-administration/index.rst26
-rw-r--r--system-administration/lego-certificates.rst131
-rw-r--r--system-administration/taler-monitoring-infrastructure.rst (renamed from taler-monitoring-infrastructure.rst)29
-rw-r--r--taler-developer-manual.rst8
-rw-r--r--taler-exchange-manual.rst179
-rw-r--r--taler-merchant-manual.rst8
-rw-r--r--taler-merchant-pos-terminal.rst13
-rw-r--r--wallet/wallet-core.md258
67 files changed, 6606 insertions, 1572 deletions
diff --git a/core/api-auditor.rst b/core/api-auditor.rst
index 61e448b8..7e0b5cb5 100644
--- a/core/api-auditor.rst
+++ b/core/api-auditor.rst
@@ -97,327 +97,3143 @@ know-your-customer (KYC) registration before issuing contracts.
time of this writing).
-.. _exchange_signkeys-list:
-------------------------------------
-Obtaining Exchange Signing Keys List
-------------------------------------
+.. fee-time-inconsistency-list:
-This API is used by auditor to obtain a list of all exchanges signing keys to be audited.
+--------------------------------------------
+Obtaining a list of fee time inconsistencies
+--------------------------------------------
-.. http:get:: /exchange-sign-keys
+This API is used to obtain a list of fee time inconsistencies
- Get a list of all exchange signing keys to be audited by the auditor.
+.. http:get:: /fee-time-inconsistency
+
+ Get a list of fee time inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`ExchangeSignKeysList` object. This request should
- virtually always be successful.
+ The auditor responds with a top level array of :ts:type:`FeeTimeInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: ExchangeSignKeysList
+ .. ts:def:: FeeTimeInconsistency
+
+ interface FeeTimeInconsistency {
+
+
+ row_id : Integer;
+
+ type : string;
+
+ time : Timestamp;
- interface ExchangeSignKeysList {
- // Exchange signing keys to be audited by the auditor.
- exchange-sign-key: ExchangeSignKeyEntry[];
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
}
- .. ts:def:: ExchangeSignKeyEntry
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. fee-time-inconsistency-update:
+
+-----------------------------------------------
+Supressing elements of fee time inconsistencies
+-----------------------------------------------
+
+This API is used to suppress select elements of fee time inconsistencies
+
+.. http:patch:: /fee-time-inconsistency
+
+ Update the 'suppressed' field of an fee time inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
- interface ExchangeSignKeyEntry {
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
- // Public online signing key of the exchange.
- exchange_pub: EddsaPublicKey;
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
- // Base URL of the exchange.
- master_sig: EddsaSignature;
+ .. ts:def:: GenericUpdate
- // Time when online signing key will first be use.
- ep_valid_from: Timestamp;
+ interface GenericUpdate {
- // Time when this online signing key will no longer be used.
- ep_expire_sign: Timestamp;
+ row_id : Integer;
- // Time when this online signing key legally expires.
- ep_expire_legal: Timestamp;
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
}
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. emergency-by-count-list:
-.. _deposit-confirmation:
+--------------------------------------
+Obtaining a list of emergency by count
+--------------------------------------
----------------------
-Deposit Confirmations
----------------------
+This API is used to obtain a list of emergency by count
-Merchants should probabilistically submit some of the deposit
-confirmations they receive from the exchange to auditors to ensure
-that the exchange does not lie about recording deposit confirmations
-with the exchange. Participating in this scheme ensures that in case
-an exchange runs into financial trouble to pay its obligations, the
-merchants that did participate in detecting the bad behavior can be
-paid out first.
+.. http:get:: /emergency-by-count
-.. http:put:: /deposit-confirmation
+ Get a list of emergency by count stored by the auditor.
- Submits a `DepositConfirmation` to the exchange. Should succeed
- unless the signature provided is invalid or the exchange is not
- audited by this auditor.
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
- :http:statuscode:`200 Ok`:
- The auditor responds with a `DepositAudited` object.
- This request should virtually always be successful.
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`EmergencyByCount` objecs.
+
:http:statuscode:`403 Forbidden`:
- The signature on the deposit confirmation is invalid.
- :http:statuscode:`410 Gone`:
- The public key used to sign the deposit confirmation
- was revoked.
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: DepositAudited
+ .. ts:def:: EmergencyByCount
+
+ interface EmergencyByCount {
+
+
+ row_id : Integer;
+
+ denompub_h : HashCode;
+
+ num_issued : Integer;
+
+ num_known : Integer;
+
+ risk : Amount;
- interface DepositAudited {
- // TODO: maybe change to ``204 No content`` instead?
+ start : Timestamp;
+
+ deposit_end : Timestamp;
+
+ value : Amount;
+
+ suppressed : boolean;
+
+
}
- .. ts:def:: DepositConfirmation
+ .. note::
- interface DepositConfirmation {
+ This API is still experimental. The API will be further developed as needed.
- // Hash over the contract for which this deposit is made.
- h_contract_terms: HashCode;
- // Hash over the extensions.
- h_extensions: HashCode;
- // Hash over the wiring information of the merchant.
- h_wire: HashCode;
- // Time when the deposit confirmation confirmation was generated.
- timestamp: Timestamp;
+.. emergency-by-count-update:
- // How much time does the merchant have to issue a refund
- // request? Zero if refunds are not allowed.
- refund_deadline: Timestamp;
+-----------------------------------------
+Supressing elements of emergency by count
+-----------------------------------------
- // By what time does the exchange have to wire the funds?
- wire_deadline: Timestamp;
+This API is used to suppress select elements of emergency by count
- // Amount to be deposited, excluding fee. Calculated from the
- // amount with fee and the fee from the deposit request.
- amount_without_fee: Amount;
+.. http:patch:: /emergency-by-count
- // Array of public keys of the deposited coins.
- coin_pubs: EddsaPublicKey[];
+ Update the 'suppressed' field of an emergency by count element according to :ts:type:`GenericUpdate`, stored by the auditor.
- // Array of deposit signatures of the deposited coins.
- // Must have the same length as ``coin_pubs``.
- coin_sigs: EddsaSignature[];
+ **Response:**
- // The Merchant's public key. Allows the merchant to later refund
- // the transaction or to inquire about the wire transfer identifier.
- merchant_pub: EddsaPublicKey;
+ :http:statuscode:`204 No Content`:
+ The element was updated.
- // Signature from the exchange of type
- // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT``.
- exchange_sig: EddsaSignature;
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
- // Public signing key from the exchange matching ``exchange_sig``.
- exchange_pub: EddsaPublicKey;
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
- // 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;
+ **Details:**
- // When does the validity of the exchange_pub end?
- ep_start: Timestamp;
+ .. ts:def:: GenericUpdate
- // When will the exchange stop using the signing key?
- ep_expire: Timestamp;
+ interface GenericUpdate {
- // When does the validity of the exchange_pub end?
- ep_end: Timestamp;
+ row_id : Integer;
- // Exchange master signature over ``exchange_sig``.
- master_sig: EddsaSignature;
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
}
.. 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 response information.
+ This API is still experimental. The API will be further developed as needed.
-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.
+
+.. row-inconsistency-list:
+
+---------------------------------------
+Obtaining a list of row inconsistencies
+---------------------------------------
+
+This API is used to obtain a list of row inconsistencies
+
+.. http:get:: /row-inconsistency
+
+ Get a list of row inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`DepositConfirmationList` object.
+ The auditor responds with a top level array of :ts:type:`RowInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: RowInconsistency
+
+ interface RowInconsistency {
+
+
+ row_id : Integer;
+
+ row_table : string;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. row-inconsistency-update:
+
+------------------------------------------
+Supressing elements of row inconsistencies
+------------------------------------------
+
+This API is used to suppress select elements of row inconsistencies
+
+.. http:patch:: /row-inconsistency
+
+ Update the 'suppressed' field of an row inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
:http:statuscode:`204 No Content`:
- No missing deposit confirmations found.
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: DepositConfirmationList
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
- interface DepositConfirmationList {
- // Deposit confirmations to be audited.
- deposit-confirmations: DepositConfirmation[];
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
}
- .. ts:def:: DepositConfirmation
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-in-inconsistency-list:
+
+----------------------------------------------
+Obtaining a list of reserve in inconsistencies
+----------------------------------------------
+
+This API is used to obtain a list of reserve in inconsistencies
+
+.. http:get:: /reserve-in-inconsistency
+
+ Get a list of reserve in inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveInInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveInInconsistency
+
+ interface ReserveInInconsistency {
+
+
+ row_id : Integer;
- interface DepositConfirmation {
+ amount_exchange_expected : Amount;
- // Database row id of entry
- row_id: Integer;
+ amount_wired : Amount;
- // Time when the deposit confirmation confirmation was generated.
- timestamp: Timestamp;
+ reserve_pub : EddsaPublicKey;
- // How much time does the merchant have to issue a refund
- // request? Zero if refunds are not allowed.
- refund_deadline: Timestamp;
+ timestamp : Timestamp;
- // By what time does the exchange have to wire the funds?
- wire_deadline: Timestamp;
+ account : string;
- // Amount to be deposited, excluding fee. Calculated from the
- // amount with fee and the fee from the deposit request.
- amount_without_fee: Amount;
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
}
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-in-inconsistency-update:
+
+-------------------------------------------------
+Supressing elements of reserve in inconsistencies
+-------------------------------------------------
-This API is used by the auditor to delete an audited deposit confirmation.
+This API is used to suppress select elements of reserve in inconsistencies
-.. http:delete:: /deposit-confirmation/$SERIAL_ID
+.. http:patch:: /reserve-in-inconsistency
- Delete deposit confirmation entry with given serial_id.
+ Update the 'suppressed' field of an reserve in inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
**Response:**
- :http:statuscode:`204 No content`:
- The deposit confirmation was deleted.
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
- :http:statuscode:`401 Unauthorized`:
- Unauthorized request.
+ .. ts:def:: GenericUpdate
- :http:statuscode:`404 Not found`:
- The deposit confirmation was unknown.
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
-.. balances-list:
+
+
+
+.. purse-not-closed-inconsistencies-list:
+
+----------------------------------------------------
+Obtaining a list of purse not closed inconsistencies
+----------------------------------------------------
+
+This API is used to obtain a list of purse not closed inconsistencies
+
+.. http:get:: /purse-not-closed-inconsistencies
+
+ Get a list of purse not closed inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`PurseNotClosedInconsistencies` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: PurseNotClosedInconsistencies
+
+ interface PurseNotClosedInconsistencies {
+
+
+ row_id : Integer;
+
+ purse_pub : EddsaPublicKey;
+
+ amount : Amount;
+
+ expiration_date : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. purse-not-closed-inconsistencies-update:
+
+-------------------------------------------------------
+Supressing elements of purse not closed inconsistencies
+-------------------------------------------------------
+
+This API is used to suppress select elements of purse not closed inconsistencies
+
+.. http:patch:: /purse-not-closed-inconsistencies
+
+ Update the 'suppressed' field of an purse not closed inconsistencies element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-not-closed-inconsistency-list:
+
+------------------------------------------------------
+Obtaining a list of reserve not closed inconsistencies
+------------------------------------------------------
+
+This API is used to obtain a list of reserve not closed inconsistencies
+
+.. http:get:: /reserve-not-closed-inconsistency
+
+ Get a list of reserve not closed inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveNotClosedInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveNotClosedInconsistency
+
+ interface ReserveNotClosedInconsistency {
+
+
+ row_id : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ balance : Amount;
+
+ expiration_time : Timestamp;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-not-closed-inconsistency-update:
+
+---------------------------------------------------------
+Supressing elements of reserve not closed inconsistencies
+---------------------------------------------------------
+
+This API is used to suppress select elements of reserve not closed inconsistencies
+
+.. http:patch:: /reserve-not-closed-inconsistency
+
+ Update the 'suppressed' field of an reserve not closed inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-balance-insufficient-inconsistency-list:
+
+----------------------------------------------------------------
+Obtaining a list of reserve balance insufficient inconsistencies
+----------------------------------------------------------------
+
+This API is used to obtain a list of reserve balance insufficient inconsistencies
+
+.. http:get:: /reserve-balance-insufficient-inconsistency
+
+ Get a list of reserve balance insufficient inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveBalanceInsufficientInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveBalanceInsufficientInconsistency
+
+ interface ReserveBalanceInsufficientInconsistency {
+
+
+ row_id : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ inconsistency_gain : boolean;
+
+ inconsistency_amount : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-balance-insufficient-inconsistency-update:
+
+-------------------------------------------------------------------
+Supressing elements of reserve balance insufficient inconsistencies
+-------------------------------------------------------------------
+
+This API is used to suppress select elements of reserve balance insufficient inconsistencies
+
+.. http:patch:: /reserve-balance-insufficient-inconsistency
+
+ Update the 'suppressed' field of an reserve balance insufficient inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. bad-sig-losses-list:
----------------------------------
-Obtaining list of auditor balances
+Obtaining a list of bad sig losses
----------------------------------
-This API is used to obtain a list of all the balances that are stored by the auditor.
+This API is used to obtain a list of bad sig losses
+
+.. http:get:: /bad-sig-losses
+
+ Get a list of bad sig losses stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+ :query operation: A string. If specified, only returns eligible rows with this :ts:type:`BadSigLosses`.operation value. The default value is NULL.
+ :query use_op_spec_pub: A boolean. If true, use the value of :ts:type:`OpSpecPub` to only return eligible rows with this :ts:type:`BadSigLosses`.operation_specific_pub value. The default value is NULL.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`BadSigLosses` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: BadSigLosses
+
+ interface BadSigLosses {
+
+
+ row_id : Integer;
+
+ operation : string;
+
+ loss : Amount;
+
+ operation_specific_pub : EddsaPublicKey;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. bad-sig-losses-update:
+
+-------------------------------------
+Supressing elements of bad sig losses
+-------------------------------------
+
+This API is used to suppress select elements of bad sig losses
+
+.. http:patch:: /bad-sig-losses
+
+ Update the 'suppressed' field of an bad sig losses element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. coin-inconsistency-list:
+
+----------------------------------------
+Obtaining a list of coin inconsistencies
+----------------------------------------
+
+This API is used to obtain a list of coin inconsistencies
+
+.. http:get:: /coin-inconsistency
+
+ Get a list of coin inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`CoinInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: CoinInconsistency
+
+ interface CoinInconsistency {
+
+
+ row_id : Integer;
+
+ operation : string;
+
+ exchange_amount : Amount;
+
+ auditor_amount : Amount;
+
+ coin_pub : EddsaPublicKey;
+
+ profitable : boolean;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. coin-inconsistency-update:
+
+-------------------------------------------
+Supressing elements of coin inconsistencies
+-------------------------------------------
+
+This API is used to suppress select elements of coin inconsistencies
+
+.. http:patch:: /coin-inconsistency
+
+ Update the 'suppressed' field of an coin inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. balances-list:
+
+----------------------------
+Obtaining a list of balances
+----------------------------
+
+This API is used to obtain a list of balances
.. http:get:: /balances
- Get a list of all balances stored by the auditor.
+ Get a list of balances stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+ :query balance_key: a string identifying a balance. If specified, only returns elements with this exact key. The default value is NULL.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`BalanceList` object.
+ The auditor responds with a top level array of :ts:type:`Balances` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
- :http:statuscode:`409 Conflict`:
- Balance missing or other error occured.
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: BalanceList
+ .. ts:def:: Balances
- interface BalanceList {
- // Total amount reported
- auditor_total_reported_balance: Amount;
+ interface Balances {
- // Amount potentially missing
- auditor_missing_balance: Amount;
+
+ row_id : Integer;
- //...
+ balance_key : string;
+
+ balance_value : Amount;
+
+ suppressed : boolean;
+
+
}
.. 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.
+ This API is still experimental. 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
+.. balances-update:
+
+-------------------------------
+Supressing elements of balances
+-------------------------------
+
+This API is used to suppress select elements of balances
+
+.. http:patch:: /balances
+
+ Update the 'suppressed' field of an balances element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denominations-without-sigs-list:
- Get a list of all pending denominations.
+----------------------------------------------
+Obtaining a list of denominations without sigs
+----------------------------------------------
+
+This API is used to obtain a list of denominations without sigs
+
+.. http:get:: /denominations-without-sigs
+
+ Get a list of denominations without sigs stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`PendingDenominationList` object.
+ The auditor responds with a top level array of :ts:type:`DenominationsWithoutSigs` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
- :http:statuscode:`204 No content`:
- No pending demoninations found.
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: PendingDenominationList
+ .. ts:def:: DenominationsWithoutSigs
+
+ interface DenominationsWithoutSigs {
+
+
+ row_id : Integer;
+
+ denompub_h : HashCode;
- interface PendingDenominationList {
- // PendingDenominationList of the auditor.
- pending_denomination: PendingDenomination[];
+ value : Amount;
+
+ start_time : Timestamp;
+
+ end_time : Timestamp;
+
+ suppressed : boolean;
+
+
}
- .. ts:def:: PendingDenomination
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
- interface PendingDenomination {
- // Balance of denomination.
- denom_balance: Amount;
- // Amount that was lost due to failures by the exchange.
- denom_loss: Amount;
+.. denominations-without-sigs-update:
- // Amount at risk of loss due to recoup operations.
- denom_risk: Amount;
+-------------------------------------------------
+Supressing elements of denominations without sigs
+-------------------------------------------------
- // Amount actually lost due to recoup operations after a revocation.
- recoup_loss: Amount;
+This API is used to suppress select elements of denominations without sigs
+
+.. http:patch:: /denominations-without-sigs
+
+ Update the 'suppressed' field of an denominations without sigs element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
}
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. misattribution-in-inconsistency-list:
+
+-----------------------------------------------------
+Obtaining a list of misattribution in inconsistencies
+-----------------------------------------------------
+
+This API is used to obtain a list of misattribution in inconsistencies
+
+.. http:get:: /misattribution-in-inconsistency
+
+ Get a list of misattribution in inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`MisattributionInInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: MisattributionInInconsistency
+
+ interface MisattributionInInconsistency {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ bank_row : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. misattribution-in-inconsistency-update:
+
+--------------------------------------------------------
+Supressing elements of misattribution in inconsistencies
+--------------------------------------------------------
+
+This API is used to suppress select elements of misattribution in inconsistencies
+
+.. http:patch:: /misattribution-in-inconsistency
+
+ Update the 'suppressed' field of an misattribution in inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. progress-list:
+
+----------------------------
+Obtaining a list of progress
+----------------------------
+
+This API is used to obtain a list of progress
+
+.. http:get:: /progress
+
+ Get a list of progress stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Progress` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Progress
+
+ interface Progress {
+
+
+ progress_key : string;
+
+ progress_offset : Integer;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. progress-update:
+
+-------------------------------
+Supressing elements of progress
+-------------------------------
+
+This API is used to suppress select elements of progress
+
+.. http:patch:: /progress
+
+ Update the 'suppressed' field of an progress element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserves-list:
+
+----------------------------
+Obtaining a list of reserves
+----------------------------
+
+This API is used to obtain a list of reserves
+
+.. http:get:: /reserves
+
+ Get a list of reserves stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Reserves` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Reserves
+
+ interface Reserves {
+
+
+ auditor_reserves_rowid : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ reserve_balance : Amount;
+
+ reserve_loss : Amount;
+
+ withdraw_fee_balance : Amount;
+
+ close_fee_balance : Amount;
+
+ purse_fee_balance : Amount;
+
+ open_fee_balance : Amount;
+
+ history_fee_balance : Amount;
+
+ expiration_date : Timestamp;
+
+ origin_account : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserves-update:
+
+-------------------------------
+Supressing elements of reserves
+-------------------------------
+
+This API is used to suppress select elements of reserves
+
+.. http:patch:: /reserves
+
+ Update the 'suppressed' field of an reserves element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. deposit-confirmations-list:
+
+-----------------------------------------
+Obtaining a list of deposit confirmations
+-----------------------------------------
+
+This API is used to obtain a list of deposit confirmations
+
+.. http:get:: /deposit-confirmations
+
+ Get a list of deposit confirmations stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DepositConfirmations` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DepositConfirmations
+
+ interface DepositConfirmations {
+
+
+ deposit_confirmation_serial_id : Integer;
+
+ h_contract_terms : HashCode;
+
+ h_policy : HashCode;
+
+ h_wire : HashCode;
+
+ exchange_timestamp : Timestamp;
+
+ refund_deadline : Timestamp;
+
+ wire_deadline : Timestamp;
+
+ total_without_fee : Amount;
+
+ coin_pubs : EddsaPublicKey;
+
+ coin_sigs : EddsaSignature;
+
+ merchant_pub : EddsaPublicKey;
+
+ exchange_sig : EddsaSignature;
+
+ exchange_pub : EddsaPublicKey;
+
+ master_sig : EddsaSignature;
+
+ suppressed : boolean;
+
+ ancient : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. deposit-confirmations-update:
+
+--------------------------------------------
+Supressing elements of deposit confirmations
+--------------------------------------------
+
+This API is used to suppress select elements of deposit confirmations
+
+.. http:patch:: /deposit-confirmations
+
+ Update the 'suppressed' field of an deposit confirmations element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denomination-key-validity-withdraw-inconsistency-list:
+
+----------------------------------------------------------------------
+Obtaining a list of denomination key validity withdraw inconsistencies
+----------------------------------------------------------------------
+
+This API is used to obtain a list of denomination key validity withdraw inconsistencies
+
+.. http:get:: /denomination-key-validity-withdraw-inconsistency
+
+ Get a list of denomination key validity withdraw inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DenominationKeyValidityWithdrawInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DenominationKeyValidityWithdrawInconsistency
+
+ interface DenominationKeyValidityWithdrawInconsistency {
+
+
+ row_id : Integer;
+
+ execution_date : Timestamp;
+
+ reserve_pub : EddsaPublicKey;
+
+ denompub_h : HashCode;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denomination-key-validity-withdraw-inconsistency-update:
+
+-------------------------------------------------------------------------
+Supressing elements of denomination key validity withdraw inconsistencies
+-------------------------------------------------------------------------
+
+This API is used to suppress select elements of denomination key validity withdraw inconsistencies
+
+.. http:patch:: /denomination-key-validity-withdraw-inconsistency
+
+ Update the 'suppressed' field of an denomination key validity withdraw inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. purses-list:
+
+--------------------------
+Obtaining a list of purses
+--------------------------
+
+This API is used to obtain a list of purses
+
+.. http:get:: /purses
+
+ Get a list of purses stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Purses` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Purses
+
+ interface Purses {
+
+
+ auditor_purses_rowid : Integer;
+
+ purse_pub : EddsaPublicKey;
+
+ balance : Amount;
+
+ target : Amount;
+
+ expiration_date : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. purses-update:
+
+-----------------------------
+Supressing elements of purses
+-----------------------------
+
+This API is used to suppress select elements of purses
+
+.. http:patch:: /purses
+
+ Update the 'suppressed' field of an purses element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. amount-arithmetic-inconsistency-list:
+
+-----------------------------------------------------
+Obtaining a list of amount arithmetic inconsistencies
+-----------------------------------------------------
+
+This API is used to obtain a list of amount arithmetic inconsistencies
+
+.. http:get:: /amount-arithmetic-inconsistency
+
+ Get a list of amount arithmetic inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`AmountArithmeticInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: AmountArithmeticInconsistency
+
+ interface AmountArithmeticInconsistency {
+
+
+ row_id : Integer;
+
+ operation : string;
+
+ exchange_amount : Amount;
+
+ auditor_amount : Amount;
+
+ profitable : boolean;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. amount-arithmetic-inconsistency-update:
+
+--------------------------------------------------------
+Supressing elements of amount arithmetic inconsistencies
+--------------------------------------------------------
+
+This API is used to suppress select elements of amount arithmetic inconsistencies
+
+.. http:patch:: /amount-arithmetic-inconsistency
+
+ Update the 'suppressed' field of an amount arithmetic inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. historic-denomination-revenue-list:
+
+-------------------------------------------------
+Obtaining a list of historic denomination revenue
+-------------------------------------------------
+
+This API is used to obtain a list of historic denomination revenue
+
+.. http:get:: /historic-denomination-revenue
+
+ Get a list of historic denomination revenue stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`HistoricDenominationRevenue` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: HistoricDenominationRevenue
+
+ interface HistoricDenominationRevenue {
+
+
+ denom_pub_hash : HashCode;
+
+ revenue_timestamp : Timestamp;
+
+ revenue_balance : Amount;
+
+ loss_balance : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. historic-denomination-revenue-update:
+
+----------------------------------------------------
+Supressing elements of historic denomination revenue
+----------------------------------------------------
+
+This API is used to suppress select elements of historic denomination revenue
+
+.. http:patch:: /historic-denomination-revenue
+
+ Update the 'suppressed' field of an historic denomination revenue element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denomination-pending-list:
+
+----------------------------------------
+Obtaining a list of denomination pending
+----------------------------------------
+
+This API is used to obtain a list of denomination pending
+
+.. http:get:: /denomination-pending
+
+ Get a list of denomination pending stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DenominationPending` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DenominationPending
+
+ interface DenominationPending {
+
+
+ denom_pub_hash : HashCode;
+
+ denom_balance : Amount;
+
+ denom_loss : Amount;
+
+ num_issued : Integer;
+
+ denom_risk : Amount;
+
+ recoup_loss : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denomination-pending-update:
+
+-------------------------------------------
+Supressing elements of denomination pending
+-------------------------------------------
+
+This API is used to suppress select elements of denomination pending
+
+.. http:patch:: /denomination-pending
+
+ Update the 'suppressed' field of an denomination pending element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. historic-reserve-summary-list:
+
+--------------------------------------------
+Obtaining a list of historic reserve summary
+--------------------------------------------
+
+This API is used to obtain a list of historic reserve summary
+
+.. http:get:: /historic-reserve-summary
+
+ Get a list of historic reserve summary stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`HistoricReserveSummary` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: HistoricReserveSummary
+
+ interface HistoricReserveSummary {
+
+
+ row_id : Integer;
+
+ start_date : Timestamp;
+
+ end_date : Timestamp;
+
+ reserve_profits : Amount;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. historic-reserve-summary-update:
+
+-----------------------------------------------
+Supressing elements of historic reserve summary
+-----------------------------------------------
+
+This API is used to suppress select elements of historic reserve summary
+
+.. http:patch:: /historic-reserve-summary
+
+ Update the 'suppressed' field of an historic reserve summary element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. exchange-signkeys-list:
+
+-------------------------------------
+Obtaining a list of exchange signkeys
+-------------------------------------
+
+This API is used to obtain a list of exchange signkeys
+
+.. http:get:: /exchange-signkeys
+
+ Get a list of exchange signkeys stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ExchangeSignkeys` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ExchangeSignkeys
+
+ interface ExchangeSignkeys {
+
+
+ exchange_pub : EddsaPublicKey;
+
+ master_sig : EddsaSignature;
+
+ ep_valid_from : Timestamp;
+
+ ep_expire_sign : Timestamp;
+
+ ep_expire_legal : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. exchange-signkeys-update:
+
+----------------------------------------
+Supressing elements of exchange signkeys
+----------------------------------------
+
+This API is used to suppress select elements of exchange signkeys
+
+.. http:patch:: /exchange-signkeys
+
+ Update the 'suppressed' field of an exchange signkeys element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. wire-format-inconsistency-list:
+
+-----------------------------------------------
+Obtaining a list of wire format inconsistencies
+-----------------------------------------------
+
+This API is used to obtain a list of wire format inconsistencies
+
+.. http:get:: /wire-format-inconsistency
+
+ Get a list of wire format inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`WireFormatInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: WireFormatInconsistency
+
+ interface WireFormatInconsistency {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ wire_offset : Integer;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. wire-format-inconsistency-update:
+
+--------------------------------------------------
+Supressing elements of wire format inconsistencies
+--------------------------------------------------
+
+This API is used to suppress select elements of wire format inconsistencies
+
+.. http:patch:: /wire-format-inconsistency
+
+ Update the 'suppressed' field of an wire format inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. refreshes-hanging-list:
+
+-------------------------------------
+Obtaining a list of refreshes hanging
+-------------------------------------
+
+This API is used to obtain a list of refreshes hanging
+
+.. http:get:: /refreshes-hanging
+
+ Get a list of refreshes hanging stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`RefreshesHanging` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: RefreshesHanging
+
+ interface RefreshesHanging {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ coin_pub : EddsaPublicKey;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. refreshes-hanging-update:
+
+----------------------------------------
+Supressing elements of refreshes hanging
+----------------------------------------
+
+This API is used to suppress select elements of refreshes hanging
+
+.. http:patch:: /refreshes-hanging
+
+ Update the 'suppressed' field of an refreshes hanging element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. emergency-list:
+
+-----------------------------
+Obtaining a list of emergency
+-----------------------------
+
+This API is used to obtain a list of emergency
+
+.. http:get:: /emergency
+
+ Get a list of emergency stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Emergency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Emergency
+
+ interface Emergency {
+
+
+ row_id : Integer;
+
+ denompub_h : HashCode;
+
+ denom_risk : Amount;
+
+ denom_loss : Amount;
+
+ deposit_start : Timestamp;
+
+ deposit_end : Timestamp;
+
+ value : Amount;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. emergency-update:
+
+--------------------------------
+Supressing elements of emergency
+--------------------------------
+
+This API is used to suppress select elements of emergency
+
+.. http:patch:: /emergency
+
+ Update the 'suppressed' field of an emergency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. closure-lags-list:
+
+--------------------------------
+Obtaining a list of closure lags
+--------------------------------
+
+This API is used to obtain a list of closure lags
+
+.. http:get:: /closure-lags
+
+ Get a list of closure lags stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ClosureLags` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ClosureLags
+
+ interface ClosureLags {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ deadline : Timestamp;
+
+ wtid : Integer;
+
+ account : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. closure-lags-update:
+
+-----------------------------------
+Supressing elements of closure lags
+-----------------------------------
+
+This API is used to suppress select elements of closure lags
+
+.. http:patch:: /closure-lags
+
+ Update the 'suppressed' field of an closure lags element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. wire-out-inconsistency-list:
+
+--------------------------------------------
+Obtaining a list of wire out inconsistencies
+--------------------------------------------
+
+This API is used to obtain a list of wire out inconsistencies
+
+.. http:get:: /wire-out-inconsistency
+
+ Get a list of wire out inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`WireOutInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: WireOutInconsistency
+
+ interface WireOutInconsistency {
+
+
+ row_id : Integer;
+
+ destination_account : string;
+
+ expected : Amount;
+
+ claimed : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. wire-out-inconsistency-update:
+
+-----------------------------------------------
+Supressing elements of wire out inconsistencies
+-----------------------------------------------
+
+This API is used to suppress select elements of wire out inconsistencies
+
+.. http:patch:: /wire-out-inconsistency
+
+ Update the 'suppressed' field of an wire out inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-balance-summary-wrong-inconsistency-list:
+
+-----------------------------------------------------------------
+Obtaining a list of reserve balance summary wrong inconsistencies
+-----------------------------------------------------------------
+
+This API is used to obtain a list of reserve balance summary wrong inconsistencies
+
+.. http:get:: /reserve-balance-summary-wrong-inconsistency
+
+ Get a list of reserve balance summary wrong inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveBalanceSummaryWrongInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveBalanceSummaryWrongInconsistency
+
+ interface ReserveBalanceSummaryWrongInconsistency {
+
+
+ row_id : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ exchange_amount : Amount;
+
+ auditor_amount : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-balance-summary-wrong-inconsistency-update:
+
+--------------------------------------------------------------------
+Supressing elements of reserve balance summary wrong inconsistencies
+--------------------------------------------------------------------
+
+This API is used to suppress select elements of reserve balance summary wrong inconsistencies
+
+.. http:patch:: /reserve-balance-summary-wrong-inconsistency
+
+ Update the 'suppressed' field of an reserve balance summary wrong inconsistency element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. row-minor-inconsistencies-list:
+
+---------------------------------------------
+Obtaining a list of row minor inconsistencies
+---------------------------------------------
+
+This API is used to obtain a list of row minor inconsistencies
+
+.. http:get:: /row-minor-inconsistencies
+
+ Get a list of row minor inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`RowMinorInconsistencies` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: RowMinorInconsistencies
+
+ interface RowMinorInconsistencies {
+
+
+ row_id : Integer;
+
+ row_table : Integer;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. row-minor-inconsistencies-update:
+
+------------------------------------------------
+Supressing elements of row minor inconsistencies
+------------------------------------------------
+
+This API is used to suppress select elements of row minor inconsistencies
+
+.. http:patch:: /row-minor-inconsistencies
+
+ Update the 'suppressed' field of an row minor inconsistencies element according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The element was updated.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ row_id : Integer;
+
+ suppressed : boolean;
+
+ // unused
+ ancient : boolean;
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
----------
Complaints
diff --git a/core/api-bank-conversion-info.rst b/core/api-bank-conversion-info.rst
index 322e9403..63856142 100644
--- a/core/api-bank-conversion-info.rst
+++ b/core/api-bank-conversion-info.rst
@@ -108,7 +108,7 @@ is used by wallets for withdrawals that involve a currency conversion.
* ``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.
+ The amount is too small to be converted because it produces an amount less than zero.
:http:statuscode:`501 Not implemented`:
This server does not support conversion, client should check config response.
@@ -154,7 +154,7 @@ is used by wallets for withdrawals that involve a currency conversion.
* ``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.
+ The amount is too small to be converted because it produces an amount less than zero.
: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 ed4d8de0..e7db1efc 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-2023 Taler Systems SA
+ Copyright (C) 2014-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -29,7 +29,7 @@ to tightly integrate with GNU Taler.
.. http:get:: /config
Return the protocol version and configuration information about the bank.
- This specification corresponds to ``current`` protocol being version **2**.
+ This specification corresponds to ``current`` protocol being **v2**.
**Response:**
@@ -47,6 +47,7 @@ to tightly integrate with GNU Taler.
// 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;
@@ -67,7 +68,7 @@ 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
+Some user interaction (on the bank's websitei) creates a
withdrawal operation record in the bank's database. The wallet can use a unique identifier
for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdrawal operation.
@@ -91,36 +92,63 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
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
+ The operation was not found.
**Details:**
.. ts:def:: BankWithdrawalOperationStatus
- export class BankWithdrawalOperationStatus {
+ interface BankWithdrawalOperationStatus {
// 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 v1
+ // @since **v1**
status: "pending" | "selected" | "aborted" | "confirmed";
// Amount that will be withdrawn with this operation
- // (raw amount without fee considerations).
- amount: Amount;
-
- // Bank account of the customer that is withdrawing, as a
- // ``payto`` URI.
+ // (raw amount without fee considerations). Only
+ // given once the amount is fixed and cannot be changed.
+ // Optional since **vC2EC**.
+ amount?: Amount;
+
+ // Suggestion for the amount to be withdrawn with this
+ // operation. Given if a suggestion was made but the
+ // user may still change the amount.
+ // Optional since **vC2EC**.
+ suggested_amount?: Amount;
+
+ // Maximum amount that the wallet can choose to withdraw.
+ // Only applicable when the amount is not fixed.
+ // @since **vC2EC**.
+ max_amount?: Amount;
+
+ // The non-Taler card fees the customer will have
+ // to pay to the bank / payment service provider
+ // they are using to make the withdrawal.
+ // @since **vC2EC**
+ card_fees?: Amount;
+
+ // Bank account of the customer that is debiting, as an
+ // RFC 8905 ``payto`` URI.
sender_wire?: string;
- // Suggestion for an exchange given by the bank.
+ // Base URL of the suggested exchange. The bank may have
+ // neither a suggestion nor a requirement for the exchange.
+ // This value is typically set in the bank's configuration.
suggested_exchange?: string;
+ // Base URL of an exchange that must be used. Optional,
+ // not given *unless* a particular exchange is mandatory.
+ // This value is typically set in the bank's configuration.
+ // @since **vC2EC**
+ required_exchange?: string;
+
// 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
+ // It may contain the withdrawal operation id.
confirm_transfer_url?: string;
// Wire transfer types supported by the bank.
@@ -128,25 +156,25 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
// Reserve public key selected by the exchange,
// only non-null if ``status`` is ``selected`` or ``confirmed``.
- // @since v1
+ // @since **v1**
selected_reserve_pub?: EddsaPublicKey;
- // Exchange account selected by the wallet
+ // Exchange account selected by the wallet;
// only non-null if ``status`` is ``selected`` or ``confirmed``.
- // @since v1
+ // @since **v1**
selected_exchange_account?: string;
- // @deprecated since v1, use ``status`` instead
+ // @deprecated since **v1**, use ``status`` instead
// Indicates whether the withdrawal was aborted.
aborted: boolean;
- // @deprecated since v1, use ``status`` instead
+ // @deprecated since **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 since v1, use ``status`` instead
+ // @deprecated since **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;
@@ -155,16 +183,27 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
.. http:post:: /withdrawal-operation/$WITHDRAWAL_ID
+ This endpoint is used by the GNU Taler wallet to supply additional
+ details needed to complete a withdraw operation.
+
**Request:**
.. ts:def:: BankWithdrawalOperationPostRequest
interface BankWithdrawalOperationPostRequest {
- // Reserve public key.
+
+ // Reserve public key that should become the wire transfer
+ // subject to fund the withdrawal.
reserve_pub: EddsaPublicKey;
- // Payto address of the exchange selected for the withdrawal.
- selected_exchange?: string;
+ // RFC 8905 (payto) address of the exchange account to be
+ // credited for the withdrawal.
+ selected_exchange: string;
+
+ // Selected amount to be transferred. Optional if the
+ // backend already knows the amount.
+ // @since **vC2EC**
+ amount?: Amount;
}
**Response:**
@@ -175,11 +214,15 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
: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`` :
+ * ``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.
+ * ``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.
+ * ``TALER_EC_BANK_AMOUNT_DIFFERS`` : the specified amount will not work for this
+ withdrawal (since **vC2EC**).
+ * ``TALER_EC_BANK_AMOUNT_REQUIRED`` : the backend requires an amount to be
+ specified (since **vC2EC**).
**Details:**
@@ -199,66 +242,25 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
// Only applicable when ``status`` is ``selected`` or ``pending``.
// It may contain withdrawal operation id
confirm_transfer_url?: string;
+
// 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/confirm
- Since protocol vC2EC
+.. http:post:: /withdrawal-operation/$WITHDRAWAL_ID/abort
- Allows a provider to notify the Bank about the payment. This will trigger a payment
- attestation at the provider, which eventually confirms the payment and allows the
- withdrawal. The API is idempotent, meaning sending a payment
- notification for the same ``WITHDRAWAL_ID`` return successfuly but not change anything.
+ Aborts ``WITHDRAWAL_ID`` operation. Has no effect on an already aborted
+ operation. This endpoint can be used by the wallet if the user aborts
+ the transaction, ensuring that the operation is also aborted at the
+ bank.
- Attention: A bank shall **never** just accept the payment directly but instead attest
- the payment using provider specific attestation logic!
+ Since protocol **v2**.
**Request:**
- .. ts:def:: BankWithdrawalConfirmationRequest
-
- interface WithdrawalConfirmationRequest {
- // The provider specific transaction identifier.
- // This identifier is used by the bank to attest the
- // payment at the providers backend.
- provider_transaction_id: string;
-
- // An identifier, which identifies the device
- // processing the payment for the withdrawal
- // triggering the confirmation (e.g. Terminal
- // or ATM). This is needed to link the withdrawal
- // to a terminal owned by a specific provider.
- // This will decide about how the withdrawal
- // is attested. The device must be known to
- // the exchange and be somehow registered.
- terminal_id: number;
-
- // The amount to withdraw. Fees are to be sent in the
- // separate field 'fees' and not included in the amount.
- amount: Amount;
-
- // The fees, the customer paid at the providers facility.
- card_fees: Amount;
- }
-
- **Response**
-
- :http:statuscode:`204 No content`:
- The payment notification was processed successfully.
- :http:statuscode:`404 Not found`:
- The withdrawal operation was not found.
- :http:statuscode:`409 Conflict`:
- The withdrawal operation has been aborted previously and can't be aborted
-
-
-.. http:post:: /withdrawal-operation/$WITHDRAWAL_ID/abort
-
- Aborts ``WITHDRAWAL_ID`` operation. Has no effect on an already aborted
- operation.
- Since protocol v2.
+ The request body is empty.
**Response:**
@@ -267,4 +269,5 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
: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.
+ The withdrawal operation has been confirmed previously and
+ can't be aborted.
diff --git a/core/api-bank-wire.rst b/core/api-bank-wire.rst
index a76f5195..84184ec9 100644
--- a/core/api-bank-wire.rst
+++ b/core/api-bank-wire.rst
@@ -133,7 +133,8 @@ Making Transactions
// time period a transaction belongs to).
timestamp: Timestamp;
- // Opaque ID of the transaction that the bank has made.
+ // Opaque ID of the wire transfer initiation performed by the bank.
+ // It is different from the /history endpoints row_id.
row_id: SafeUint64;
}
@@ -429,6 +430,25 @@ exposed by bank gateways in production.
// time period a transaction belongs to).
timestamp: Timestamp;
- // Opaque ID of the transaction that the bank has made.
+ // Opaque ID of the wire transfer initiation performed by the bank.
+ // It is different from the /history endpoints row_id.
row_id: SafeUint64;
}
+
+
+Security Considerations
+=======================
+
+For implementors:
+
+* The withdrawal operation ID must contain enough entropy to be unguessable.
+
+Design:
+
+* The user must complete the 2FA step of the withdrawal in the context of their banking
+ app or online banking Website.
+ We explicitly reject any design where the user would have to enter a confirmation code
+ they get from their bank in the context of the wallet, as this would teach and normalize
+ bad security habits.
+
+
diff --git a/core/api-challenger.rst b/core/api-challenger.rst
index 759d4e70..afb3096d 100644
--- a/core/api-challenger.rst
+++ b/core/api-challenger.rst
@@ -120,6 +120,19 @@ Receiving Configuration
// @since v0, may become mandatory in the future.
implementation?: string;
+ // @since v2.
+ // Object; map of keys (names of the fields of the address
+ // to be entered by the user) to objects with a "regex" (string)
+ // containing an extended Posix regular expression for allowed
+ // address field values, and a "hint"/"hint_i18n" giving a
+ // human-readable explanation to display if the value entered
+ // by the user does not match the regex. Keys that are not mapped
+ // to such an object have no restriction on the value provided by
+ // the user. See "ADDRESS_RESTRICTIONS" in the challenger configuration.
+ restrictions: Object;
+
+ // @since v2.
+ address_type: "email" | "phone";
}
.. _challenger-setup:
@@ -216,15 +229,8 @@ Login
.. ts:def:: ChallengeStatus
interface ChallengeStatus {
- // Object; map of keys (names of the fields of the address
- // to be entered by the user) to objects with a "regex" (string)
- // containing an extended Posix regular expression for allowed
- // address field values, and a "hint"/"hint_i18n" giving a
- // human-readable explanation to display if the value entered
- // by the user does not match the regex. Keys that are not mapped
- // to such an object have no restriction on the value provided by
- // the user. See "ADDRESS_RESTRICTIONS" in the challenger configuration.
- restrictions: Object;
+ // @deprecated since v2, use /config
+ restrictions?: Object;
// indicates if the given address cannot be changed anymore, the
// form should be read-only if set to true.
@@ -232,11 +238,29 @@ Login
// form values from the previous submission if available, details depend
// on the ``ADDRESS_TYPE``, should be used to pre-populate the form
- last_address: Object;
+ last_address?: Object;
// number of times the address can still be changed, may or may not be
// shown to the user
changes_left: Integer;
+
+ // when we would re-transmit the challenge the next
+ // time (at the earliest) if requested by the user
+ // only present if challenge already created
+ // @since v2
+ retransmission_time: Timestamp;
+
+ // how many times might the PIN still be retransmitted
+
+ // how many times might the PIN still be retransmitted
+ // only present if challenge already created
+ // @since v2
+ pin_transmissions_left?: Integer;
+
+ // how many times might the user still try entering the PIN code
+ // only present if challenge already created
+ // @since v2
+ auth_attempts_left?: Integer;
}
@@ -259,10 +283,17 @@ Challenge
**Response:**
:http:statuscode:`200 OK`:
- If the request ask for application/json the response is `ChallengeCreateResponse`. Since protocol **v1**.
+ If the request ask for application/json the response is `ChallengeResponse`. Since protocol **v2**.
Otherwise, the body contains a form asking for the answer to
the challenge to be entered by the user using the
template :ref:`enter-tan-form <challenger_enter-tan-form>`.
+ :http:statuscode:`302 Found`:
+ Only possible if request didn't ask for application/json. Since protocol **v2**.
+ 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:`400 Bad Request`:
The request does not follow the spec.
If the request ask for application/json the response will include error
@@ -287,9 +318,21 @@ Challenge
code, hint and detail. Since protocol **v1**.
Otherwise, the body contains information using the template :ref:`internal-error <challenger_internal-error>`.
+ .. ts:def:: ChallengeResponse
+
+ interface ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
+
+ .. ts:def:: ChallengeRedirect
+
+ // @since v2
+ interface ChallengeRedirect {
+ // challenge is completed, use should redirect here
+ redirect_url: string;
+ }
+
.. ts:def:: ChallengeCreateResponse
- interface ChallengeCreateResponse {
+ interface ChallengeCreateResponse {
// how many more attempts are allowed, might be shown to the user,
// highlighting might be appropriate for low values such as 1 or 2 (the
@@ -304,10 +347,13 @@ Challenge
// might make a useful hint to the user
transmitted: boolean;
- // timestamp explaining when we would re-transmit the challenge the next
- // time (at the earliest) if requested by the user
- next_tx_time: string;
+ // @deprecated in v2, use retransmission_time
+ next_tx_time?: string;
+ // when we would re-transmit the challenge the next
+ // time (at the earliest) if requested by the user
+ // @since v2
+ retransmission_time: Timestamp;
}
@@ -330,7 +376,10 @@ Solve
**Response:**
+ :http:statuscode:`200 OK`:
+ If the request ask for application/json the response is `ChallengeSolveResponse`. Since protocol **v2**.
:http:statuscode:`302 Found`:
+ Only possible if request didn't ask for application/json. Since protocol **v2**.
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``),
@@ -360,6 +409,10 @@ Solve
code, hint and detail. Since protocol **v1**.
Otherwise, the body contains information using the template :ref:`internal-error <challenger_internal-error>`.
+ .. ts:def:: ChallengeSolveResponse
+
+ type ChallengeSolveResponse = ChallengeRedirect | InvalidPinResponse;
+
.. ts:def:: InvalidPinResponse
interface InvalidPinResponse {
diff --git a/core/api-common.rst b/core/api-common.rst
index 521b2fc0..2208eb8d 100644
--- a/core/api-common.rst
+++ b/core/api-common.rst
@@ -255,6 +255,11 @@ hashed data. See `base32`_.
// 32-byte hash code.
type ShortHashCode = string;
+.. ts:def:: AccountAccessToken
+
+ // 32-byte nonce.
+ type AccountAccessToken = string;
+
.. ts:def:: WireSalt
// 16-byte salt.
@@ -758,6 +763,56 @@ denoting microseconds since the UNIX Epoch. ``UINT64_MAX`` represents "never".
uint64_t abs_value_us__; // in network byte order
};
+.. _LegalTrouble:
+
+451 Responses
+^^^^^^^^^^^^^
+
+When KYC operations are required, various endpoints may respond with a
+``451 Unavailable for Legal Reasons`` status code and a `LegitimizationNeededResponse`
+body.
+
+.. ts:def:: LegitimizationNeededResponse
+
+ // Implemented in this style since exchange
+ // protocol **v20**.
+ interface LegitimizationNeededResponse {
+
+ // 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 for which KYC
+ // is required.
+ h_payto: PaytoHash;
+
+ // Public key associated with the account. The client must sign
+ // the initial request for the KYC status using the corresponding
+ // private key. Will be either a reserve public key or a merchant
+ // (instance) public key.
+ //
+ // Absent if no public key is currently associated
+ // with the account and the client MUST thus first
+ // credit the exchange via an inbound wire transfer
+ // to associate a public key with the debited account.
+ account_pub?: EddsaPublicKey;
+
+ // Identifies a set of measures that were triggered and that are
+ // now preventing this operation from proceeding. Gives the
+ // account holder a starting point for understanding why the
+ // transaction was blocked and how to lift it. The account holder
+ // should use the number to check for the account's AML/KYC status
+ // using the ``/kyc-check/$REQUIREMENT_ROW`` endpoint.
+ requirement_row: Integer;
+
+ }
+
+
Cryptographic primitives
^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/core/api-corebank.rst b/core/api-corebank.rst
index 680b2724..f9ab7caa 100644
--- a/core/api-corebank.rst
+++ b/core/api-corebank.rst
@@ -88,7 +88,7 @@ Config
// @since v4, will become mandatory in the next version.
bank_name?: string;
- // Advertised base URL to use when you sharing an URL with another
+ // Advertised base URL to use when you sharing an URL with another
// program.
// @since v4.
base_url?: string;
@@ -188,6 +188,11 @@ Account Management
// Only admin can set this property.
debit_threshold?: Amount;
+ // If present, set a custom minimum cashout amount for this account.
+ // Only admin can set this property
+ // @since v4
+ min_cashout?: 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.
@@ -220,6 +225,7 @@ Account Management
* ``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_MIN_CASHOUT`` : a non-admin user has tried to create an account with a custom min cashout amount.
* ``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``.
@@ -233,28 +239,6 @@ Account Management
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
@@ -288,6 +272,11 @@ Account Management
// Only admin can change this property.
debit_threshold?: Amount;
+ // If present, change the custom minimum cashout amount for this account.
+ // Only admin can set this property
+ // @since v4
+ min_cashout?: Amount;
+
// If present, enables 2FA and set the TAN channel used for challenges
tan_channel?: TanChannel;
}
@@ -306,6 +295,7 @@ Account Management
* ``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_NON_ADMIN_SET_MIN_CASHOUT`` : a non-admin user has tried to change their custom min cashout amount.
* ``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``.
@@ -343,6 +333,29 @@ Account Management
* ``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.
+
+.. _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-list:
.. http:get:: /public-accounts
@@ -460,6 +473,11 @@ Account Management
// Number indicating the max debit allowed for the requesting user.
debit_threshold: Amount;
+ // Custom minimum cashout amount for this account.
+ // If null or absent, the global conversion fee is used.
+ // @since v4
+ min_cashout?: Amount;
+
// Is this account visible to anyone?
is_public: boolean;
@@ -472,7 +490,7 @@ Account Management
// Current status of the account
// active: the account can be used
- // deleted: the account has been deleted but is retained for compliance
+ // 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.
@@ -513,6 +531,11 @@ Account Management
// Number indicating the max debit allowed for the requesting user.
debit_threshold: Amount;
+ // Custom minimum cashout amount for this account.
+ // If null or absent, the global conversion fee is used.
+ // @since v4
+ min_cashout?: Amount;
+
// Addresses where to send the TAN for transactions.
// Currently only used for cashouts.
// If missing, cashouts will fail.
@@ -539,7 +562,7 @@ Account Management
// Current status of the account
// active: the account can be used
- // deleted: the account has been deleted but is retained for compliance
+ // 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.
@@ -682,8 +705,8 @@ Transactions
row_id: Integer;
}
-Taler Withdrawals
------------------
+Account withdrawals
+-------------------
.. http:post:: /accounts/$USERNAME/withdrawals
@@ -691,12 +714,7 @@ Taler Withdrawals
**Request:**
- .. ts:def:: BankAccountCreateWithdrawalRequest
-
- interface BankAccountCreateWithdrawalRequest {
- // Amount to withdraw.
- amount: Amount;
- }
+ The request must be a `BankAccountCreateWithdrawalRequest`.
**Response:**
@@ -709,6 +727,28 @@ Taler Withdrawals
**Details:**
+ .. ts:def:: BankAccountCreateWithdrawalRequest
+
+ interface BankAccountCreateWithdrawalRequest {
+
+ // Amount to withdraw. If given, the wallet
+ // cannot change the amount.
+ // Optional since **vC2EC**.
+ amount?: Amount;
+
+ // Suggested amount to withdraw. The wallet can
+ // still change the suggestion.
+ // @since **vC2EC**
+ suggested_amount?: Amount;
+
+ // The non-Taler card fees the customer will have
+ // to pay to the account owner, bank and/or
+ // payment service provider
+ // they are using to make this withdrawal.
+ // @since **vC2EC**
+ card_fees?: Amount;
+ }
+
.. ts:def:: BankAccountCreateWithdrawalResponse
interface BankAccountCreateWithdrawalResponse {
@@ -866,9 +906,10 @@ Cashouts
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.
+ * ``TALER_EC_BANK_BAD_CONVERSION``: exchange rate was calculated incorrectly by the client.
+ * ``TALER_EC_BANK_BANK_CONVERSION_AMOUNT_TO_SMALL``: the amount of the cashout is too small.
+ * ``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.
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
index 4c5be000..31d46158 100644
--- a/core/api-exchange.rst
+++ b/core/api-exchange.rst
@@ -62,7 +62,7 @@ possibly by using HTTPS.
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**.
+ This specification corresponds to ``current`` protocol being **v20**.
**Response:**
@@ -1323,19 +1323,188 @@ to allow exchange staff to monitor suspicious transactions
and freeze or unfreeze accounts suspected of money laundering.
-.. http:get:: /aml/$OFFICER_PUB/decisions/$STATE
+.. http:get:: /aml/$OFFICER_PUB/measures
- Obtain list of AML decisions (filtered by $STATE). ``$STATE`` must be either ``normal``, ``pending`` or ``frozen``.
+ To enable the AML staff SPA to give AML staff a choice of possible measures, a
+ new endpoint ``/aml/$OFFICER_PUB/measures`` is added that allows the AML SPA
+ to dynamically GET the list of available measures. It returns a list of known
+ KYC checks (by name) with their descriptions and a list of AML programs with
+ information about the required context.
- *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 endpoint was introduced in protocol **v20**.
- :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.
+ **Request:**
- **Response**
+ *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:**
+
+ :http:statuscode:`200 Ok`:
+ Information about possible measures is returned in a
+ `AvailableMeasureSummary` object.
+
+ **Details:**
+
+ .. ts:def:: AvailableMeasureSummary
+
+ interface AvailableMeasureSummary {
+
+ // Available original measures that can be
+ // triggered directly by default rules.
+ roots: { "$measure_name" : MeasureInformation; };
+
+ // Available AML programs.
+ programs: { "$prog_name" : AmlProgramRequirement; };
+
+ // Available KYC checks.
+ checks: { "$check_name" : KycCheckInformation; };
+
+ }
+
+ .. ts:def:: MeasureInformation
+
+ interface MeasureInformation {
+
+ // Name of a KYC check.
+ check_name: string;
+
+ // Name of an AML program.
+ prog_name: string;
+
+ // Context for the check. Optional.
+ context?: Object;
+
+ }
+
+ .. ts:def:: AmlProgramRequirement
+
+ interface AmlProgramRequirement {
+
+ // Description of what the AML program does.
+ description: string;
+
+ // List of required field names in the context to run this
+ // AML program. SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using this program.
+ context: string[];
+
+ // List of required attribute names in the
+ // input of this AML program. These attributes
+ // are the minimum that the check must produce
+ // (it may produce more).
+ inputs: string[];
+
+ }
+
+ .. ts:def:: KycCheckInformation
+
+ interface KycCheckInformation {
+
+ // Description of the KYC check. Should be shown
+ // to the AML staff but will also be shown to the
+ // client when they initiate the check in the KYC SPA.
+ description: string;
+ description_i18n: {};
+
+ // Names of the fields that the CONTEXT must provide
+ // as inputs to this check.
+ // SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using
+ // this check.
+ requires: string[];
+
+ // Names of the attributes the check will output.
+ // SPA must check that the outputs match the
+ // required inputs when combining a KYC check
+ // with an AML program into a measure.
+ outputs: string[];
+
+ // Name of a root measure taken when this check fails.
+ fallback: string;
+ }
+
+.. http:get:: /aml/$OFFICER_PUB/kyc-statistics/$NAME
+
+ Returns the number of KYC events matching the given event type ``$NAME`` in
+ the specified time range. Note that this query can be slow as the
+ statistics are computed on-demand. (This is OK as such requests should be
+ rare.)
+
+ This endpoint was introduced in protocol **v20**.
+
+ **Request:**
+
+ *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.
+
+ :query start_date=TIMESTAMP:
+ *Optional*. Specifies the date when to
+ start looking (inclusive). If not given, the start time of the
+ exchange operation is used.
+ :query end_date=TIMESTAMP:
+ *Optional*. Specifies the date when to
+ stop looking (exclusive). If not given, the current date is used.
+
+ **Response:**
:http:statuscode:`200 OK`:
- The responds will be an `AmlRecords` message.
+ The responds will be an `EventCounter` message.
+
+ **Details:**
+
+ .. ts:def:: EventCounter
+
+ interface EventCounter {
+ // Number of events of the specified type in
+ // the given range.
+ counter: Integer;
+ }
+
+
+.. http:get:: /aml/$OFFICER_PUB/decisions
+
+ **Request:**
+
+ *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 endpoint was introduced in this form in protocol **v20**.
+
+ :query limit:
+ *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 offset:
+ *Optional*. Row number threshold, see ``delta`` for its
+ interpretation. Defaults to ``INT64_MAX``, namely the biggest row id
+ possible in the database.
+ :query h_payto:
+ *Optional*. Account selector. All matching accounts are returned if this
+ filter is absent, otherwise only decisions for this account.
+ :query active:
+ *Optional*. If set to yes, only return active decisions, if no only
+ decisions that have been superceeded. Do not give (or use "all") to
+ see all decisions regardless of activity status.
+ :query investigation:
+ *Optional*. If set to yes, only return accounts that are under
+ AML investigation, if no only accounts that are not under investigation.
+ Do not give (or use "all") to see all accounts regardless of
+ investigation status.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The responds will be an `AmlDecisions` message.
:http:statuscode:`204 No content`:
There are no matching AML records.
:http:statuscode:`403 Forbidden`:
@@ -1347,100 +1516,217 @@ and freeze or unfreeze accounts suspected of money laundering.
**Details:**
- .. ts:def:: AmlRecords
+ .. ts:def:: AmlDecisions
- interface AmlRecords {
+ interface AmlDecisions {
- // Array of AML records matching the query.
- records: AmlRecord[];
+ // Array of AML decisions matching the query.
+ records: AmlDecision[];
}
- .. ts:def:: AmlRecord
+ .. ts:def:: AmlDecision
- interface AmlRecord {
+ interface AmlDecision {
// Which payto-address is this record about.
// Identifies a GNU Taler wallet or an affected bank account.
h_payto: PaytoHash;
- // What is the current AML state.
- current_state: Integer;
+ // Row ID of the record. Used to filter by offset.
+ rowid: Integer;
- // Monthly transaction threshold before a review will be triggered
- threshold: Amount;
+ // Justification for the decision.
+ justification: string;
- // RowID of the record.
- rowid: Integer;
+ // When was the decision made?
+ decision_time: Timestamp;
+
+ // Free-form properties about the account.
+ // Can be used to store properties such as PEP,
+ // risk category, type of business, hits on
+ // sanctions lists, etc.
+ properties?: AccountProperties;
+
+ // What are the new rules?
+ limits: LegitimizationRuleSet;
+
+ // True if the account is under investigation by AML staff
+ // after this decision.
+ to_investigate: boolean;
+
+ // True if this is the active decision for the
+ // account.
+ is_active: boolean;
}
+ .. ts:def:: AccountProperties
-.. http:get:: /aml/$OFFICER_PUB/decision/$H_PAYTO
+ // All fields in this object are optional. The actual
+ // properties collected depend fully on the discretion
+ // of the exchange operator;
+ // however, some common fields are standardized
+ // and thus described here.
+ interface AccountProperties {
- Obtain deails about an AML decision.
+ // True if this is a politically exposed account.
+ // Rules for classifying accounts as politically
+ // exposed are country-dependent.
+ pep?: boolean;
- *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.
+ // True if this is a sanctioned account.
+ // Rules for classifying accounts as sanctioned
+ // are country-dependent.
+ sanctioned?: boolean;
- :query history: *Optional*. If set to yes, we return all historic decisions and not only the last one.
+ // True if this is a high-risk account.
+ // Rules for classifying accounts as at-risk
+ // are exchange operator-dependent.
+ high_risk?: boolean;
- **Response**
+ // Business domain of the account owner.
+ // The list of possible business domains is
+ // operator- or country-dependent.
+ business_domain?: string;
- :http:statuscode:`200 OK`:
- 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 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.
+ // Is the client's account currently frozen?
+ is_frozen?: boolean;
- **Details:**
+ // Was the client's account reported to the authorities?
+ was_reported?: boolean;
- .. ts:def:: AmlDecisionDetails
+ }
- interface AmlDecisionDetails {
+ .. ts:def:: LegitimizationRuleSet
+
+ interface LegitimizationRuleSet {
+
+ // When does this set of rules expire and
+ // we automatically transition to the successor
+ // measure?
+ expiration_time: 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[];
+ // Name of the measure to apply when the expiration time is
+ // reached. If not set, we refer to the default
+ // set of rules (and the default account state).
+ successor_measure?: string;
+
+ // Legitimization rules that are to be applied
+ // to this account.
+ rules: KycRule[];
+
+ // Custom measures that KYC rules and the
+ // ``successor_measure`` may refer to.
+ custom_measures: { "$measure_name" : MeasureInformation; };
- // Array of KYC attributes obtained for this account.
- kyc_attributes: KycDetail[];
}
- .. ts:def:: AmlDecisionDetail
+ .. ts:def:: KycRule
- interface AmlDecisionDetail {
+ interface KycRule {
- // What was the justification given?
- justification: string;
+ // Type of operation to which the rule applies.
+ operation_type: string;
- // FIXME: review!
- // What is the new AML state.
- new_state: Integer;
+ // The measures will be taken if the given
+ // threshold is crossed over the given timeframe.
+ threshold: Amount;
- // When was this decision made?
- decision_time: Timestamp;
+ // Over which duration should the ``threshold`` be
+ // computed. All amounts of the respective
+ // ``operation_type`` will be added up for this
+ // duration and the sum compared to the ``threshold``.
+ timeframe: RelativeTime;
+
+ // Array of names of measures to apply.
+ // Names listed can be original measures or
+ // custom measures from the `AmlOutcome`.
+ // A special measure "verboten" is used if the
+ // threshold may never be crossed.
+ measures: string[];
+
+ // If multiple rules apply to the same account
+ // at the same time, the number with the highest
+ // rule determines which set of measures will
+ // be activated and thus become visible for the
+ // user.
+ display_priority: Integer;
+
+ // True if the rule (specifically, operation_type,
+ // threshold, timeframe) and the general nature of
+ // the measures (verboten or approval required)
+ // should be exposed to the client.
+ // Defaults to "false" if not set.
+ exposed?: boolean;
+
+ // True if all the measures will eventually need to
+ // be satisfied, false if any of the measures should
+ // do. Primarily used by the SPA to indicate how
+ // the measures apply when showing them to the user;
+ // in the end, AML programs will decide after each
+ // measure what to do next.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean;
+
+ }
+
+.. http:get:: /aml/$OFFICER_PUB/attributes/$H_PAYTO
+
+ Obtain attributes obtained as part of AML/KYC processes for a
+ given account.
+
+ This endpoint was introduced in protocol **v20**.
+
+ **Request:**
+
+ *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.
+
+ :query limit:
+ *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 offset:
+ *Optional*. Row number threshold, see ``delta`` for its
+ interpretation. Defaults to ``INT64_MAX``, namely the biggest row id
+ possible in the database.
+
+ **Response:**
- // What is the new AML decision threshold (in monthly transaction volume)?
- new_threshold: Amount;
+ :http:statuscode:`200 OK`:
+ The responds will be an `KycAttributes` message.
+ :http:statuscode:`204 No content`:
+ There are no matching KYC attributes.
+ :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.
+
+ .. ts:def:: KycAttributes
- // Who made the decision?
- decider_pub: AmlOfficerPublicKeyP;
+ interface KycAttributes {
+
+ // Matching KYC attribute history of the account.
+ details: KycAttributeCollectionEvent[];
}
- .. ts:def:: KycDetail
+ .. ts:def:: KycAttributeCollectionEvent
- interface KycDetail {
+ interface KycAttributeCollectionEvent {
- // Name of the configuration section that specifies the provider
- // which was used to collect the KYC details
- // FIXME: review!
- provider_section: string;
+ // Row ID of the record. Used to filter by offset.
+ rowid: Integer;
+
+ // Name of the provider
+ // which was used to collect the attributes. NULL if they were
+ // just uploaded via a form by the account owner.
+ provider_name?: string;
// The collected KYC data. NULL if the attribute data could not
// be decrypted (internal error of the exchange, likely the
@@ -1450,9 +1736,6 @@ and freeze or unfreeze accounts suspected of money laundering.
// Time when the KYC data was collected
collection_time: Timestamp;
- // Time when the validity of the KYC data will expire
- expiration_time: Timestamp;
-
}
@@ -1487,17 +1770,17 @@ and freeze or unfreeze accounts suspected of money laundering.
// Human-readable justification for the decision.
justification: string;
- // At what monthly transaction volume should the
- // decision be automatically reviewed?
- new_threshold: Amount;
-
// Which payto-address is the decision about?
// Identifies a GNU Taler wallet or an affected bank account.
h_payto: PaytoHash;
- // What is the new AML state (e.g. frozen, unfrozen, etc.)
- // Numerical values are defined in `AmlDecisionState`.
- new_state: Integer;
+ // What are the new rules?
+ // New since protocol **v20**.
+ new_rules: LegitimizationRuleSet;
+
+ // True if the account should remain under investigation by AML staff.
+ // New since protocol **v20**.
+ keep_investigating: boolean;
// Signature by the AML officer over a `TALER_AmlDecisionPS`.
// Must have purpose ``TALER_SIGNATURE_MASTER_AML_KEY``.
@@ -1506,9 +1789,6 @@ and freeze or unfreeze accounts suspected of money laundering.
// When was the decision made?
decision_time: Timestamp;
- // Optional argument to impose new KYC requirements
- // that the customer has to satisfy to unblock transactions.
- kyc_requirements?: string[];
}
@@ -1769,7 +2049,7 @@ Batch Withdraw
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.
+ The response will be an `LegitimizationNeededResponse` object.
Implementation note: internally, we need to
distinguish between upgrading the reserve to an
@@ -1857,29 +2137,6 @@ Batch Withdraw
}
- .. ts:def:: KycNeededRedirect
-
- interface KycNeededRedirect {
-
- // 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/$REQUIREMENT_ROW/...`` endpoint.
- requirement_row: Integer;
-
- }
-
.. ts:def:: WithdrawError
interface WithdrawError {
@@ -1986,7 +2243,7 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
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.
+ The response will be an `LegitimizationNeededResponse` object.
.. ts:def:: AgeWithdrawRequest
@@ -3970,6 +4227,7 @@ typically also view the balance.)
// Current AML state for the target account. Non-zero
// values indicate that the transfer is blocked due to
// AML enforcement.
+ // Removed in protocol **v20**.
aml_decision: Integer;
// True if the KYC check for the merchant has been
@@ -4465,7 +4723,7 @@ Wallet-to-wallet transfers
: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.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
@@ -4576,7 +4834,7 @@ Wallet-to-wallet transfers
: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.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
@@ -4869,6 +5127,17 @@ regulatory compliance.
.. http:post:: /kyc-wallet
+ The ``/wallet-kyc`` POST endpoint allows a wallet to notify an exchange if
+ it will cross a balance threshold. Here, the ``balance`` specified should be
+ the threshold (from the ``wallet_balance_limit_without_kyc`` array) that the
+ wallet would cross, and *not* the *exact* balance of the wallet. The exchange
+ will respond with a wire target UUID. The wallet can then use this UUID to
+ being the KYC process at ``/kyc-check/``. The wallet must only proceed to
+ obtain funds exceeding the threshold after the KYC process has concluded.
+ While wallets could be "hacked" to bypass this measure (we cannot
+ cryptographically enforce this), such modifications are a terms of service
+ violation which may have legal consequences for the user.
+
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
@@ -4892,7 +5161,7 @@ regulatory compliance.
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.
+ The response will be a `LegitimizationNeededResponse` object (changed in protocol **v20**).
**Details:**
@@ -4915,73 +5184,74 @@ regulatory compliance.
reserve_pub: EddsaPublicKey;
}
- .. ts:def:: WalletKycUuid
+.. http:get:: /kyc-check/$REQUIREMENT_ROW
- interface WalletKycUuid {
+ Checks the KYC status of a particular payment target and possibly begins a
+ KYC process by allowing the customer to choose the next KYC measure to
+ satisfy. This endpoint is typically used by wallets or merchants that
+ have been told that a transaction is not happening because it triggered
+ some KYC/AML measure and now want to check how the KYC/AML
+ requirement could be fulfilled (or whether it already has been
+ statisfied and the operation can now proceed). Long-polling may be used
+ to instantly observe a change in the KYC requirement status.
- // UUID that the wallet should use when initiating
- // the KYC check.
- requirement_row: number;
+ The requirement row of the ``/kyc-check/`` endpoint encodes the
+ legitimization measure's serial number. It is returned in
+ `LegitimizationNeededResponse` responses via the ``requirement_row`` field.
- // Hash of the payto:// account URI for the wallet.
- h_payto: PaytoHash;
-
- }
+ Given a valid pair of requirement row and account owner signature, the
+ ``/kyc-check/`` endpoint returns either just the KYC status or redirects the
+ client (202) to the next required stage of the KYC process. The redirection
+ must be for an HTTP(S) endpoint to be triggered via a simple HTTP GET. It
+ must always be the same endpoint for the same client, as the wallet/merchant
+ backend are not required to check for changes to this endpoint. Clients
+ that received a 202 status code may repeat the request and use long-polling
+ to detect a change of the HTTP status.
+ This endpoint exists in this specific form only since protocol **v20**.
-.. http:get:: /kyc-check/$REQUIREMENT_ROW/$H_PAYTO/$USERTYPE
-
- Checks the KYC status of a particular payment target and possibly begins the
- KYC process. This endpoint is used by wallets or merchants that have been
- told about a KYC requirement and now want to check if the KYC requirement
- has been fulfilled. Long-polling may be used to instantly observe a change
- in the KYC requirement status.
+ **Request:**
- 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".
+ *Account-Owner-Signature*:
- **Request:**
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$ACCOUNT_PRIV``, affirming the desire to obtain KYC data. Note that
+ this is merely a simple authentication mechanism, the details of the
+ request are not protected by the signature. The ``$ACCOUNT_PRIV`` is
+ either the (wallet long-term) reserve private key or the merchant instance
+ private key.
: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
- is already legitimized. Note that the legitimization would be
- triggered by another request to the same endpoint with a valid
- ``token``.
+ wait up to ``timeout_ms`` milliseconds if the requirement continues
+ to be mandatory provisioning of KYC data by the client.
+ Ignored if the HTTP status code is already ``200 Ok``. Note that
+ clients cannot long-poll for AML staff actions, so status information
+ about an account being under AML review needs to be requested
+ periodically.
**Response:**
:http:statuscode:`200 Ok`:
- The KYC operation succeeded, the exchange confirms that the
- payment target already authorized to transact.
- The response will be an `AccountKycStatus` object.
+ No mandatory KYC actions are required by the client at this time.
+ The client *may* still visit the KYC URL to initiate voluntary checks.
+ The response will be an `AccountKycStatus` object which specifies
+ restrictions that currently apply to the account. If the
+ client attempts to exceed *soft* limits, the status may change
+ to a ``202 Accepted``. Hard limits cannot be lifted by passing KYC checks.
:http:statuscode:`202 Accepted`:
- The user should be redirected to the provided location to perform
+ The account holder performed an operation that would have crossed
+ *soft* limits and must be redirected to the provided location to perform
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.
+ The response will be an `AccountKycStatus` object.
:http:statuscode:`204 No content`:
The exchange is not configured to perform KYC and thus
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.
+ The provided signature is not acceptable for the requirement row.
: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.
+ The requirement row is unknown.
**Details:**
@@ -4989,75 +5259,274 @@ regulatory compliance.
interface AccountKycStatus {
- // Details about the KYC check that the user
- // passed.
- kyc_details: KycDetails;
+ // Current AML state for the target account. True if
+ // operations are not happening due to staff processing
+ // paperwork *or* due to legal requirements (so the
+ // client cannot do anything but wait).
+ //
+ // Note that not every AML staff action may be legally
+ // exposed to the client, so this is merely a hint that
+ // a client should be told that AML staff is currently
+ // reviewing the account. AML staff *may* review
+ // accounts without this flag being set!
+ aml_review: boolean;
+
+ // Access token needed to construct the ``/kyc-spa/``
+ // URL that the user should open in a browser to
+ // proceed with the KYC process (optional if the status
+ // type is ``200 Ok``, mandatory if the HTTP status
+ // is ``202 Accepted``).
+ access_token: AccountAccessToken;
+
+ // Array with limitations that currently apply to this
+ // account and that may be increased or lifted if the
+ // KYC check is passed.
+ // Note that additional limits *may* exist and not be
+ // communicated to the client. If such limits are
+ // reached, this *may* be indicated by the account
+ // going into ``aml_review`` state. However, it is
+ // also possible that the exchange may legally have
+ // to deny operations without being allowed to provide
+ // any justification.
+ // The limits should be used by the client to
+ // possibly structure their operations (e.g. withdraw
+ // what is possible below the limit, ask the user to
+ // pass KYC checks or withdraw the rest after the time
+ // limit is passed, warn the user to not withdraw too
+ // much or even prevent the user from generating a
+ // request that would cause it to exceed hard limits).
+ limits?: AccountLimit[];
+
+ }
+
+ .. ts:def:: AccountLimit
+
+ interface AccountLimit {
+
+ // Operation that is limited.
+ // Must be one of "WITHDRAW", "DEPOSIT", "P2P-RECEIVE"
+ // or "WALLET-BALANCE".
+ operation_type: string;
+
+ // Timeframe during which the limit applies.
+ timeframe: RelativeTime;
+
+ // Maximum amount allowed during the given timeframe.
+ // Zero if the operation is simply forbidden.
+ threshold: Amount;
- // Current time of the exchange, used as part of
- // what the exchange signs over.
- now: Timestamp;
+ // True if this is a soft limit that could be raised
+ // by passing KYC checks. Clients *may* deliberately
+ // try to cross limits and trigger measures resulting
+ // in 451 responses to begin KYC processes.
+ // Clients that are aware of hard limits *should*
+ // inform users about the hard limit and prevent flows
+ // in the UI that would cause violations of hard limits.
+ soft_limit: boolean;
+ }
+
+.. http:get:: /kyc-spa/$ACCESS_TOKEN
+.. http:get:: /kyc-spa/$FILENAME
+
+ A set of ``/kyc-spa/$ACCESS_TOKEN`` GET endpoints is created per account
+ hash that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint
+ will in principle redirect clients. The KYC SPA will use the
+ ``$ACCESS_TOKEN`` of its URL to initialize itself via the
+ ``/kyc-info/$ACCESS_TOKEN`` endpoint family. The KYC SPA may download
+ additional resources via ``/kyc-spa/$FILENAME``. The filenames must not
+ match base32-encoded 256-bit values.
+
+ This endpoint was introduced in protocol **v20**.
+
+
+.. http:get:: /kyc-info/$ACCESS_TOKEN
+
+ The ``/kyc-info/$ACCESS_TOKEN`` endpoints are created per client
+ account hash (but access controlled via a unique target token)
+ to return information about the state of the KYC or AML process
+ to the KYC SPA. The SPA uses this information to show the user an
+ appropriate dialog. The SPA should also long-poll this endpoint for changes
+ to the AML/KYC state. Note that this is a client-facing endpoint, so it will
+ only provide a restricted amount of information to the customer (as some
+ laws may forbid us to inform particular customers about their true status).
+ The endpoint will typically inform the SPA about possible choices to
+ proceed, such as directly uploading files, contacting AML staff, or
+ proceeding with a particular KYC process at an external provider (such as
+ Challenger). If the user chooses to initate a KYC process at an external
+ provider, the SPA must request the respective process to be set-up by the
+ exchange via the ``/kyc-start/`` endpoint.
+
+ This endpoint was introduced in protocol **v20**.
- // EdDSA signature of the exchange affirming the account
- // is KYC'ed, must be of purpose
- // ``TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS``
- // and over ``TALER_AccountSetupStatusSignaturePS``.
- exchange_sig: EddsaSignature;
+ **Request:**
- // public key used to create the signature.
- exchange_pub: EddsaPublicKey;
+ *If-None-Match*:
+ The client MAY provide an ``If-None-Match`` header with an ETag.
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- aml_status: Integer;
+ :query timeout_ms=MILLISECONDS:
+ *Optional.* If specified, the exchange will wait up to MILLISECONDS for
+ a change to a more recent legitimization measure before returning a 304
+ Not Modified status.
+ **Response:**
+
+ *Etag*: Will be set to the serial ID of the measure. Used for long-polling (only for 200 OK responses).
+
+ :http:statuscode:`200 OK`:
+ The body is a `KycProcessClientInformation`.
+ :http:statuscode:`204 No Content`:
+ There are no open KYC requirements or possible voluntary checks
+ the client might perform.
+ :http:statuscode:`304 Not Modified`:
+ The KYC requirements did not change.
+
+
+ **Details:**
+
+ .. ts:def:: KycProcessClientInformation
+
+ interface KycProcessClientInformation {
+
+ // List of requirements.
+ requirements?: { name : KycRequirementInformation};
+
+ // True if the client is expected to eventually satisfy all requirements.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean
+
+ // List of available voluntary checks the client could pay for.
+ // Since **vATTEST**.
+ voluntary_checks?: { name : KycCheckInformation};
}
- .. ts:def:: AccountKycRedirect
+ .. ts:def:: KycRequirementInformation
- interface AccountKycRedirect {
+ interface KycRequirementInformation {
- // URL that the user should open in a browser to
- // proceed with the KYC process.
- kyc_url: string;
+ // Which form should be used? Common values include "INFO"
+ // (to just show the descriptions but allow no action),
+ // "LINK" (to enable the user to obtain a link via
+ // ``/kyc-start/``) or any build-in form name supported
+ // by the SPA.
+ form: string;
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- aml_status: Integer;
+ // English description of the requirement.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n ?: { [lang_tag: string]: string };
+
+ // ID of the requirement, useful to construct the
+ // ``/kyc-upload/$ID`` or ``/kyc-start/$ID`` endpoint URLs.
+ // Present if and only if "form" is not "INFO". The
+ // ``$ID`` value may itself contain ``/`` or ``?`` and
+ // basically encode any URL path (and optional arguments).
+ id?: string;
}
- .. ts:def:: AccountAmlBlocked
+ .. ts:def:: KycCheckInformation
- interface AccountAmlBlocked {
+ // Since **vATTEST**.
+ interface KycCheckInformation {
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- aml_status: Integer;
+ // English description of the check.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n ?: { [lang_tag: string]: string };
+ // FIXME: is the above in any way sufficient
+ // to begin the check? Do we not need at least
+ // something more??!?
}
- .. ts:def:: KycDetails
- // Object that specifies which KYC checks are satisfied.
- interface KycDetails {
+.. http:post:: /kyc-upload/$ID
+
+ The ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload
+ client-provided evidence. The ``$ID`` will be provided as part of the
+ ``/kyc-info`` body. This is for checks of type ``FORM``. In practice,
+ ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of the selected
+ measure (but this should be irrelevant for the client).
+
+ This endpoint was introduced in protocol **v20**.
- // Keys are the names of the check(s).
- // The values are for now always empty objects.
+ **Request:**
+
+ Basically oriented along the possible formats of a HTTP form being
+ POSTed. Details will depend on the form. The server will try to decode the
+ uploaded body from whatever format it is provided in.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The information was successfully uploaded. The SPA should fetch
+ an updated ``/kyc-info/``.
+ :http:statuscode:`404 Not Found`:
+ The ``$ID`` is unknown to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The upload conflicts with a previous upload.
+ :http:statuscode:`413 Request Entity Too Large`:
+ The body is too large.
+
+.. http:post:: /kyc-start/$ID
+
+ The ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new external
+ KYC process. It will return the URL that the client must GET to begin the
+ KYC process. The SPA should probably open this URL in a new window or tab.
+ The ``$ID`` will be provided as part of the ``/kyc-info`` body. In
+ practice, ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of
+ the selected measure (but this should be irrelevant for the client).
+
+ **Request:**
+
+ Use empty JSON body for now.
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The KYC process was successfully initiated. The URL is in a
+ `KycProcessStartInformation` object.
+ :http:statuscode:`404 Not Found`:
+ The ``$ID`` is unknown to the exchange.
+
+ **Details:**
+
+ .. ts:def:: KycProcessStartInformation
+
+ interface KycProcessStartInformation {
+
+ // URL to open.
+ redirect_url: string;
}
-.. http:get:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO
+ .. note::
+
+ As this endpoint is involved in every KYC check at the beginning, this
+ is also the place where we could integrate the payment process for the KYC fee
+ in the future (since **vATTEST**).
+
+.. http:get:: /kyc-proof/$PROVIDER_NAME?state=$H_PAYTO
+
+ Upon completion of the process at the external KYC provider, the provider
+ must redirect the client (browser) to trigger a GET request to a new
+ ``/kyc-proof/$H_PAYTO/$PROVIDER_NAME`` endpoint. Once this endpoint is
+ triggered, the exchange will pass the received arguments to the respective
+ logic plugin. The logic plugin will then (asynchronously) update the KYC
+ status of the user. The logic plugin should redirect the user to the KYC
+ SPA. This endpoint deliberately does not use the ``$ACCESS_TOKEN`` as the
+ external KYC provider should not learn that token.
- Endpoint accessed from the user's browser at the *end* of a
+ This endpoint is thus accessed from the user's browser at the *end* of a
KYC process, possibly providing the exchange with additional
credentials to obtain the results of the KYC process.
Specifically, the URL arguments should provide
information to the exchange that allows it to verify that the
user has completed the KYC process. The details depend on
- the logic, which is selected by the "$PROVIDER_SECTION".
+ the logic, which is selected by the "$PROVIDER_NAME".
While this is a GET (and thus safe, and idempotent), the operation
may actually trigger significant changes in the exchange's state.
@@ -5071,8 +5540,10 @@ regulatory compliance.
If the KYC plugin logic is OAuth 2.0, the query parameters are:
- :query code=CODE: OAuth 2.0 code argument.
- :query state=STATE: OAuth 2.0 state argument with the H_PAYTO.
+ :query code=CODE:
+ OAuth 2.0 code argument.
+ :query state=STATE:
+ OAuth 2.0 state argument with the H_PAYTO.
.. note::
@@ -5102,8 +5573,8 @@ regulatory compliance.
service within a reasonable time period.
-.. http:get:: /kyc-webhook/$PROVIDER_SECTION/*
-.. http:post:: /kyc-webhook/$PROVIDER_SECTION/*
+.. http:get:: /kyc-webhook/$PROVIDER_NAME/*
+.. http:post:: /kyc-webhook/$PROVIDER_NAME/*
.. http:get:: /kyc-webhook/$LOGIC/*
.. http:post:: /kyc-webhook/$LOGIC/*
@@ -5111,7 +5582,7 @@ regulatory compliance.
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".
+ "$PROVIDER_NAME".
**Request:**
@@ -5176,7 +5647,7 @@ designated account.
: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.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
@@ -5392,7 +5863,7 @@ designated account.
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.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index 439e7177..0e45cbad 100644
--- a/core/api-merchant.rst
+++ b/core/api-merchant.rst
@@ -123,7 +123,7 @@ such as the implemented version of the protocol and the currency used.
.. http:get:: /config
Return the protocol version and currency supported by this merchant backend.
- This specification corresponds to ``current`` protocol being version **v14**.
+ This specification corresponds to ``current`` protocol being version **v15**.
**Response:**
@@ -342,6 +342,11 @@ Making the payment
// payment.
pos_confirmation?: string;
+ // Signed tokens. Returned in the same order as the
+ // token envelopes were provided in the request.
+ // @since protocol **vSUBSCRIBE**
+ token_sigs?: SignedTokenEnvelope[];
+
}
.. ts:def:: PayRequest
@@ -350,22 +355,17 @@ Making the payment
// The coins used to make the payment.
coins: CoinPaySig[];
- // Index of the selected choice within the ``choices`` array of
- // the contract terms.
- // @since protocol **vSUBSCRIBE**
- choice_index?: Integer;
-
// Input tokens required by choice indicated by ``choice_index``.
// @since protocol **vSUBSCRIBE**
- tokens: TokenUseSig[];
+ tokens?: TokenUseSig[];
// Array of output tokens to be (blindly) signed by the merchant.
// Output tokens specified in choice indicated by ``choice_index``.
// @since protocol **vSUBSCRIBE**
- tokens_evs: TokenEnvelope[];
+ tokens_evs?: TokenEnvelope[];
// Custom inputs from the wallet for the contract.
- wallet_data?: Object;
+ wallet_data?: PayWalletData;
// The session for which the payment is made (or replayed).
// Only set for session-based payments.
@@ -373,6 +373,59 @@ Making the payment
}
+ .. ts:def:: SignedTokenEnvelope
+
+ interface SignedTokenEnvelope {
+
+ // Blind signature made by the merchant.
+ blind_sig: TokenIssueBlindSig;
+
+ // Hash of the token issue public key.
+ h_issue: HashCode;
+
+ }
+
+ .. ts:def:: TokenIssueBlindSig
+
+ type TokenIssueBlindSig = RSATokenIssueBlindSig | CSTokenIssueBlindSig;
+
+ .. ts:def:: RSATokenIssueBlindSig
+
+ interface RSATokenIssueBlindSig {
+ cipher: "RSA";
+
+ // (blinded) RSA signature
+ blinded_rsa_signature: BlindedRsaSignature;
+ }
+
+ .. ts:def:: CSTokenIssueBlindSig
+
+ interface CSTokenIssueBlindSig {
+ 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;
+
+ }
+
+ .. ts:def:: PayWalletData
+
+ interface PayWalletData {
+ // Index of the selected choice within the ``choices`` array of
+ // the contract terms.
+ // @since protocol **vSUBSCRIBE**
+ choice_index?: Integer;
+
+ // Output commitment. Hash over output token envelopes.
+ // @since protocol **vSUBSCRIBE**
+ h_outputs?: HashCode;
+ }
+
.. ts:def:: CoinPaySig
export interface CoinPaySig {
@@ -418,27 +471,41 @@ Making the payment
interface TokenUseSig {
- // Signature on ``TALER_DepositRequestPS`` with the public key of the
- // token being provisioned to the merchant.
+ // Signature on ``TALER_TokenUseRequestPS`` with the token use key of
+ // the token being used in this request.
token_sig: EddsaSignature;
- // Public key of the token being provisioned to the merchant.
+ // Token use public key.
token_pub: EddsaPublicKey;
- // Unblinded signature made by the token family public key of the merchant.
+ // Unblinded signature on ``TALER_TokenIssueRequestPS`` with the token
+ // issue public key of the merchant.
ub_sig: UnblindedSignature;
- // The hash of the token family public key associated with this token.
- h_denom: HashCode;
+ // The hash of the token issue public key of the token being using
+ // in this request.
+ h_issue: HashCode;
}
.. ts:def:: TokenEnvelope
+ interface TokenEnvelope {
+
+ // Blinded token use public key.
+ token_ev: BlindedTokenEnvelope;
+
+ // Hash of the token issue public key.
+ h_issue: HashCode;
+
+ }
+
+ .. ts:def:: BlindedTokenEnvelope
+
// 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;
+ type BlindedTokenEnvelope = RSATokenEnvelope | CSTokenEnvelope;
.. ts:def:: RSATokenEnvelope
@@ -1851,6 +1918,7 @@ Inspecting inventory
image: ImageDataUrl;
// A list of taxes paid by the merchant for one unit of this product.
+ // Optional since **v15**.
taxes: Tax[];
// Number of units of the product in stock in sum in total,
@@ -1866,7 +1934,8 @@ Inspecting inventory
total_lost: Integer;
// Identifies where the product is in stock.
- address: Location;
+ // Optional since **v15**.
+ address?: Location;
// Identifies when we expect the next restocking to happen.
next_restock?: Timestamp;
@@ -1876,6 +1945,93 @@ Inspecting inventory
}
+.. http:get:: [/instances/$INSTANCE]/private/pos
+
+ This is used to return the point-of-sale (POS) configuration with full details on all items in the inventory.
+
+ Endpoint was introduced in protocol **v15**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the inventory. Returns
+ a `FullInventoryDetailsResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: FullInventoryDetailsResponse
+
+ interface FullInventoryDetailsResponse {
+
+ // List of products that are present in the inventory.
+ products: MerchantPosProductDetail[];
+
+ // List of categories in the inventory.
+ categories: MerchantCategory[];
+
+ }
+
+ .. ts:def:: MerchantPosProductDetail
+
+ interface MerchantPosProductDetail {
+
+ // A unique numeric ID of the product
+ product_serial: number;
+
+ // A merchant-internal unique identifier for the product
+ product_id?: string;
+
+ // A list of category IDs this product belongs to.
+ // Typically, a product only belongs to one category, but more than one is supported.
+ categories: number[];
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n: { [lang_tag: string]: string };
+
+ // Unit in which the product is measured (liters, kilograms, packages, etc.).
+ unit: string;
+
+ // The price for one ``unit`` of the product. Zero is used
+ // to imply that this product is not sold separately, or
+ // that the price is not fixed, and must be supplied by the
+ // front-end. If non-zero, this price MUST include applicable
+ // taxes.
+ price: Amount;
+
+ // An optional base64-encoded product image.
+ image?: ImageDataUrl;
+
+ // A list of taxes paid by the merchant for one unit of this product.
+ taxes?: Tax[];
+
+ // Number of units of the product in stock in sum in total,
+ // including all existing sales ever. Given in product-specific
+ // units.
+ // Optional, if missing treat as "infinite".
+ total_stock?: Integer;
+
+ // Minimum age buyer must have (in years).
+ minimum_age?: Integer;
+
+ }
+
+ .. ts:def:: MerchantCategory
+
+ interface MerchantCategory {
+ // A unique numeric ID of the category
+ id: number;
+
+ // The name of the category. This will be shown to users and used in the order summary.
+ name: string;
+
+ // Map from IETF BCP 47 language tags to localized names
+ name_i18n?: { [lang_tag: string]: string };
+ }
+
+
Reserving inventory
-------------------
diff --git a/core/api-terminal.rst b/core/api-terminal.rst
new file mode 100644
index 00000000..91d9b7e6
--- /dev/null
+++ b/core/api-terminal.rst
@@ -0,0 +1,356 @@
+..
+ This file is part of GNU TALER.
+
+ Copyright (C) 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
+
+.. _terminal-api:
+
+============
+Terminal API
+============
+
+.. contents:: Table of Contents
+ :local:
+
+Introduction
+------------
+
+Terminals are devices where users can withdraw digital cash.
+
+This API is offered by a payment service backend and is used by such
+terminals. It enables imposing limits on withdrawals per unique user ID (and
+communicating such limits to the terminals) as well as setting up and
+triggering withdrawal operations.
+
+Implementations of this API typically interact with a terminal-specific
+payment service (or a bank) to realize the service.
+
+
+Authentication
+--------------
+
+Terminals must authenticate against all terminal API using basic auth according to `HTTP basic auth <https://tools.ietf.org/html/rfc7617>`_.
+
+
+Config
+------
+
+.. 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`:
+ Response is a `TerminalConfig`.
+
+ **Details:**
+
+ .. ts:def:: TerminalConfig
+
+ interface TerminalConfig {
+ // Name of the API.
+ name: "taler-terminal";
+
+ // 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;
+
+ // Terminal provider display name to be used in user interfaces.
+ provider_name: string;
+
+ // The currency supported by this Terminal-API
+ // must be the same as the currency specified
+ // in the currency field of the wire gateway config
+ currency: string;
+
+ // Wire transfer type supported by the terminal.
+ // FIXME: needed?
+ wire_type: string;
+ }
+
+
+.. http:get:: /quotas/$UUID
+
+ Obtain the current transaction limit for the given $UUID.
+ The UUID should be an encoding of a unique identifier of
+ the user.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ Response is a `WithdrawLimit`.
+
+ **Details:**
+
+ .. ts:def:: WithdrawLimit
+
+ interface WithdrawLimit {
+ // Maximum amount that can be withdrawn now.
+ limit: Amount;
+
+ // Time when the limit may increase.
+ expiration: Timestamp;
+ }
+
+
+.. http:post:: /quotas/$UUID/lock
+
+ This endpoint allows a terminal to reserve a given amount
+ from the user's quota, ensuring that a subsequent operation
+ will not fail due to a quota violation.
+
+ **Request:**
+
+ The request should be a `WithdrawLimitLock`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The change was accepted.
+ :http:statuscode:`409 Conflict`:
+ The proposed lock would push the user above the limit.
+
+ **Details:**
+
+ .. ts:def:: WithdrawLimitLock
+
+ interface WithdrawLimitLock {
+
+ // Amount that should be reserved from the quota.
+ limit: Amount;
+
+ // ID for the lock. FIXME: could also be 32-byte nonce?
+ lock: string;
+
+ // How long should the lock be held?
+ expiration: Timestamp;
+ }
+
+.. http:delete:: /quotas/$UUID/lock/$LOCK
+
+ This endpoint allows the terminal to clear a lock it may have
+ previously created.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The lock was cleared.
+ :http:statuscode:`404 Not found`:
+ The lock is unknown.
+ :http:statuscode:`409 Conflict`:
+ The lock was already used in a withdrawal operation.
+
+
+.. http:post:: /withdrawals
+
+ This endpoint allows the terminal to set up a new withdrawal
+ operation.
+
+ **Request:**
+
+ The request should be a `TerminalWithdrawalSetup`.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The operation was created. The response will be
+ a `TerminalWithdrawalSetupResponse`.
+ :http:statuscode:`404 Not found`:
+ A lock was specified but the lock is not known for
+ the given user.
+ :http:statuscode:`409 Conflict`:
+ A conflicting withdrawal operation already exists or
+ the amount would violate the quota for the specified user.
+
+ **Details:**
+
+ .. ts:def:: TerminalWithdrawalSetup
+
+ interface TerminalWithdrawalSetup {
+
+ // Amount to withdraw. If given, the wallet
+ // cannot change the amount!
+ amount?: Amount;
+
+ // Suggested amount to withdraw. If given, the wallet can
+ // still change the suggestion.
+ suggested_amount?: Amount;
+
+ // A provider-specific transaction identifier.
+ // This identifier may be used to attest the
+ // payment at the provider's backend. Optional,
+ // as we may not know it at this time.
+ provider_transaction_id?: string;
+
+ // The non-Taler fees the customer will have
+ // to pay to the service provider
+ // they are using to make this withdrawal.
+ // If the fees cannot be precalculated,
+ // they can be specified in the /withdrawals/$WITHDRAWAL_ID/check
+ // request after the transaction was executed.
+ terminal_fees?: Amount;
+
+ // Unique request ID to make retried requests idempotent.
+ request_uid: string;
+
+ // Unique user ID of the user. Optional
+ // in case a user Id is not (yet) known.
+ user_uuid?: string;
+
+ // ID identifying a lock on the quota that the client
+ // may wish to use in this operation. May only be
+ // present if ``user_uuid`` is also given.
+ lock?: string;
+ }
+
+ .. ts:def:: TerminalWithdrawalSetupResponse
+
+ interface TerminalWithdrawalSetupResponse {
+
+ // ID identifying the withdrawal operation being created.
+ withdrawal_id: string;
+ }
+
+
+.. http:post:: /withdrawals/$WITHDRAWAL_ID/check
+
+ Endpoint for providers to notify the terminal backend about a payment having
+ happened. This will cause the bank to validate the transaction and allow
+ the withdrawal to proceed. The API is idempotent, meaning sending a payment
+ notification for the same ``WITHDRAWAL_ID`` return successfuly but not
+ change anything. This endpoint is always *optional*: the bank, exchange and
+ wallet should all eventually notice the wire transfer with or without this
+ endpoint being called. However, by calling this endpoint checks that might
+ otherwise only happen periodically can be triggered immediately.
+
+ The endpoint may also be used to associate a user ID at a very late stage
+ with the withdrawal --- and still get an immediate failure if we are above
+ the quota.
+
+ .. note::
+
+ The backend shall **never** just accept this claim that the payment was
+ confirmed, but instead needs to internally attest that the payment was
+ successful using provider-specific transaction validation logic! The point
+ of this endpoint is merely to trigger this validation **now**.
+
+ **Request:**
+
+ The body of the request must be a `TerminalWithdrawalConfirmationRequest`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The payment notification was processed successfully.
+ :http:statuscode:`404 Not found`:
+ The withdrawal operation was not found.
+ :http:statuscode:`409 Conflict`:
+ The withdrawal operation has been previously aborted
+ and cannot be confirmed anymore.
+ :http:statuscode:`451 Unavailable for Legal Reasons`:
+ The withdrawal operation cannot be confirmed because
+ it would put the user above the legal limit. The
+ payment service will eventually bounce the transfer
+ (if it were to become effective), but the user should
+ already be shown an error.
+
+ **Details:**
+
+ .. ts:def:: TerminalWithdrawalConfirmationRequest
+
+ interface TerminalWithdrawalConfirmationRequest {
+
+ // A provider-specific transaction identifier.
+ // This identifier may be used to facilitate the
+ // backend to check that the credit was confirmed.
+ provider_transaction_id?: string;
+
+ // The fees which the customer had to pay to the
+ // provider
+ terminal_fees?: Amount;
+
+ // A user-specific identifier for quota checks.
+ user_uuid?: string;
+
+ // ID identifying a lock on the quota that the client
+ // may wish to use in this operation. May only be
+ // present if ``user_uuid`` is also given.
+ lock?: string;
+ }
+
+.. http:get:: /withdrawals/$WITHDRAWAL_ID
+
+ Query information about a withdrawal, identified by the ``WITHDRAWAL_ID``.
+
+ **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 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.
+
+.. http:delete:: /withdrawals/$WITHDRAWAL_ID/abort
+
+ Aborts ``WITHDRAWAL_ID`` operation. Has no effect on an already aborted
+ operation. This endpoint can be used by the terminal if the terminal aborts
+ the transaction, ensuring that the operation is also aborted at the
+ bank.
+
+ **Request:**
+
+ The request body is empty.
+
+ **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.
+
+
+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:: /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``.
diff --git a/core/index-bank-apis.rst b/core/index-bank-apis.rst
index f108df32..1097126a 100644
--- a/core/index-bank-apis.rst
+++ b/core/index-bank-apis.rst
@@ -33,6 +33,7 @@ Bank RESTful APIs
api-bank-revenue
api-bank-integration
api-bank-conversion-info
+ api-terminal
.. toctree::
:hidden:
diff --git a/design-documents/023-taler-kyc.rst b/design-documents/023-taler-kyc.rst
index 77dbe2bb..786b0ca7 100644
--- a/design-documents/023-taler-kyc.rst
+++ b/design-documents/023-taler-kyc.rst
@@ -46,12 +46,11 @@ Taler needs to take *measures* based on the following primary *triggers*:
* Import of new sanctions lists and triggering of measures against matches of existing
customer records against the list
-For the different operation types, there can be both soft
-and hard limits. Soft limits are those that the customer
-may raise by providing data and passing KYC checks.
-Hard limits cannot be lifted, for example because an
-exchange forbids crossing those limits in its terms of
-service for all customers.
+For the different operation types, there can be both soft and hard
+limits. Soft limits are those that the customer may raise by providing data
+and passing KYC checks. Hard limits cannot be lifted, for example because an
+exchange forbids crossing those limits in its terms of service for all
+customers.
Process requirements
@@ -113,7 +112,8 @@ For each account we must:
* define risk-profile (902.4, 905.1)
* document the specific setup, likely not just the INI file
-* should have some key AMLA file attributes, such as:
+* should have some key Anti-Money-Laundering Act (AMLA)
+ file attributes, such as:
* File opened, file closed (keep data for X years afterwards!)
* low-risk or high-risk business relationship
@@ -146,6 +146,44 @@ be tracked in the system statistics:
* account frozen
* account unfrozen
* account closed
+* sanction list import / update
+
+
+TODO: Sanction lists
+^^^^^^^^^^^^^^^^^^^^
+
+We need to be able to import new sanction lists (whenever they are published)
+and then check existing AMLA files against those lists. Additionally, newly
+created AMLA files must be checked against the current list and some "measure"
+applied in case of a match.
+
+This will primarily require us to define an endpoint to upload a sanction list
+and to define a new table to track the list of sanctioned entities. As it is
+expected that sanction lists will not permit fully automated determinations in
+all cases, an external "sanction check" program should be configured which
+compares records against the current list and determines the correct measure,
+such as no change, further manual review by AML staff, or even automatic
+freeze (and report) depending on how well the records match.
+
+Basically, the "sanction check" program takes the sanction list and an
+attribute set to compute the same kind of `AmlOutcome` that an AML program
+outputs given a context and an attribute set.
+
+
+Security requirements
+^^^^^^^^^^^^^^^^^^^^^
+
+IBANs are predictable. We (probably) do not want random people to be able to
+initate KYC processes for other parties. Similarly, the attestation API
+requires us to somehow *authenticate* the user to ensure we only give out
+attestation data to the data subject themselves. For P2P payments and
+withdrawals, we have the reserve public key that is only known to the data
+subject and thus can be used to authenticate the client via a signature. Only
+pure deposits (by merchants or directly from a wallet) are a problem as the
+only thing we know about the receiver is the IBAN at that time, and literally
+any user could just deposit money into some bank account, so knowledge of the
+IBAN is insufficient to determine that we actually are communicating with the
+owner of the bank account.
Further considerations
@@ -162,46 +200,80 @@ user for *voluntary* KYC processes related to attestation (#7365).
Proposed Solution
=================
-We allow certain *conditions* to *trigger* a single specific *measures*.
-For the different *measures*, we define:
-
-* Who has to do something (AML staff, user, nobody)
-* Contextual input data to be provided (with templating, e.g. amount set dynamically based on the *trigger*)
-* A *check* to be performed (user-interactive or staff-interactive)
-* Another *measure* to take on failure of a user-interactive check
-* A *program* that uses data from the *check* as well as *context* data
- to determine an *outcome* which is the specific operational state
- (normal, held on staff, held on user, frozen, etc.) the account is to transition to
-* What information about the state to show to the user (normal, information required, pending, etc.)
-
-For the user-interactive checks we need a SPA (for KYC) that is given:
+The main state of an account is represented by a set of `KYC rules <KycRule>` (the
+`LegitimizationRuleSet`) which specify the current *rules* to apply to
+transactions involving the account. Rules can *exposed* to the account owner,
+or can be secret. Each *rule* specifies certain *conditions* which, if met,
+*trigger* a set of *measures*. After a *rule* was *triggered* and
+before the *outcome* of a respective *measure* has been produced (say
+because the user did not yet enter their data or the AML officer is still
+reviewing the case), the existing rules remain in force. Rules have a display
+priority, and if a second rule with a higher display priority is also
+triggered, the *measures* of the higher-priority rule become the active
+*measures*. Except for the default rule set, every legitimization rule set
+also has an *expiration* time after which a successor *measure* (or the
+default rule set) is automatically triggered.
+
+For any possible *measures*, we define:
+
+* Contextual input data to be provided (with dynamic inputs,
+ e.g. amount set dynamically based on the *trigger* could be
+ in the context)
+* A *check* to be performed (checks can be user-interactive (LINK, FORM)
+ or staff-interactive (INFO))
+* A fallback *measure* to take on failure of a user-interactive check
+ (if the check fails, we cannot run the AML *program* as required inputs
+ might be missing!)
+* An (AML) *program* that uses *attribtes* from the *check* as well as
+ *context* data to determine an *outcome* represented as the
+ `AmlOutcome`.
+
+"verboten" is the name of a special *measure*, which means that crossing the
+respective transaction threshold is categorically not allowed (for this
+account). "verboten" with a threshold of zero can be used to freeze funds.
+
+Possible *outcomes* of a measure include:
+
+* The next operational state (normal, AML investigation) of the account
+ (basically, whether to add it to the work list of AML staff).
+* A new set of *rules* in the form of a `LegitimizationRuleSet` that
+ determines custom rules to apply to transactions involving the account;
+ such rules may be used to block certain transactions by using the
+ "verboten" measure. The `LegitimizationRuleSet` also must specify
+ an *expiration* time by which we fall back to a successor measure
+ *or* to the default rules.
+* A (largely) free-form set of `AccountProperties` that AML staff can
+ use to tag accounts with. Some default properties are defined, but
+ the exchange does not do anything with these and AML SPAs are free to
+ use any properties they like. Account properties are only exposed
+ to AML staff and never to the customer.
+* A set of *events* that are to be added to the timeline of the
+ operator for statistical purposes.
+
+For the user-interactive *checks* we need a KYC SPA that is given:
* instructions to render (with either a form to fill or links to external checks);
here the context could provide an array of choices!
-* possibly an external check that was set up (if any); for cost-reasons, we should only do one at a time,
- and probably should then always redirect the browser to that check.
+* possibly an external check that was set up (if any); for cost-reasons, we
+ should only do one at a time, and probably should then always redirect the
+ browser to that check.
-For the staff-interactive checks we need a SPA (for AML):
+For the staff-interactive *checks* we need an AML SPA:
* to file forms and upload documentation (without state transition)
* to decide on next measure (providing context); here, the exchange needs
to expose the list of available *measures* and required *context* for each
-For non-interactive measures (normal operation, account frozen) we need:
-
-* Expiration time (in context)
-* Measure to trigger upon expiration, again with context
- (renew documents, resume normal operation, etc.)
-
We need some customer-driven interactivity in KYB/KYC process, for example the
user may need to be given choices (address vs. phone, individual vs. business,
order in which to provide KYC data of beneficiaries). As a result, the
-exchange needs to serve some SPA for measures where the user is shown the next
-step(s) or choices (which person to collect KYC data on, whether to run
+exchange needs to serve some SPA for *measures* where the user is shown the
+next step(s) or choices (which person to collect KYC data on, whether to run
challenger on phone number of physical address, etc.). The SPA should also
potentially contain a form to allow the customer to directly upload documents
to us (like business registration) instead of to some KYC provider. This is
-because KYC providers may not be flexible enough.
+because KYC providers may not be flexible enough. The SPA should also allow
+the customer to perform KYC checks voluntarily.
Similarly, the AML staff will need to be able to trigger rather complex
KYB/KYC processes, like "need KYC on X and Y and Z" or "phone number or
@@ -213,80 +285,172 @@ documents.
Terminology
^^^^^^^^^^^
-* **Check**: A check establishes a particular attribute of a user, such as their name based on an ID document and lifeness, mailing address, phone number, taxpayer identity, etc. Checks may be given *context* (such as whether a customer is an individual or a business) to run correctly. Checks can also be AML staff inserting information for plausibilization. Checks result in an *outcome* being decided by an external AML *program*.
-
-* **Condition**: A condition specifies when KYC is required. Conditions include the *type of operation*, a threshold amount (e.g. above EUR:1000) and possibly a time period (e.g. over the last month).
-
-* **Configuration**: The configuration determines the *legitimization rules*, and specifies which providers offer which *checks* at what *cost*.
-
-* **Context**: Context is information provided as input into a *check* and *program* to customize their execution. The context is initially set by the *trigger*, but may evolve as the *account* undergoes *measures*. For each *check* and *program*, the required *context* data must be specified.
-
-* **Cost**: How much would a client have to pay for a KYC process (if they voluntarily choose to do so for attestation).
-
-* **Expiration**: KYC legitimizations may be outdated. Expiration rules determine when *checks* have to be performed again.
-
-* **Legitimization rules**: The legitimization rules determine under which *conditions* which *checks* must be performend and the *expiration* time period for the *checks*.
-
-* **Logic**: Logic refers to a specific bit of code (realized as an exchange plugin) that enables the interaction with a specific *provider*. Logic typically requires *configuration* for access control (such as an authorization token) and possibly the endpoint of the specific *provider* implementing the respective API.
-
-* **Measure**: Describes the possible outgoing edges from one state in the state machine (including how to show the current state). Each edge is given some *context* and a *check* to be performed as well as a *program* to decide the *outcome* and the next *measure*.
-
-* **Outcome**: Describes the account state that an account ends up in due to the result of a *check*. Outcomes can be that an account is frozen (no transactions possible until freeze expires), held (no transactions possible until another *measure* has been taken), or operating normally. Outcomes also include a new set of *legitimization rules* to apply and an expiration time at which point a new *measure* will be automatically taken. Finally, parts of the outcome may be explained to the client (for example, to allow a wallet to stay below hard withdraw thresholds).
-
-* **Provider**: A provider performs a specific set of *checks* at a certain *cost*. Interaction with a provider is performed by provider-specific *logic*.
-
-* **Program**: An AML helper *program* is given *context* about the current state of an account and the attribute data from a *check* to compute the *outcome*. For example, a *program* may look at the "PEP" field of a KYC check and decide if the outcome is to put the account into ``normal`` or ``held-for-manual-review`` state.
-
-* **Type of operation**: The operation type determines which Taler-specific operation has triggered the KYC requirement. We support four types of operation: withdraw (by customer), deposit (by merchant), P2P receive (by wallet) and (high) wallet balance.
+* **Attributes**: Attributes are used to represent KYC data obtained about
+ an account holder. Attributes include passport images, address data,
+ business registration documents, and indeed arbitrary forms filed by
+ AML staff or the customer themselves. Attribute data is considered
+ sensitive private information and is thus stored encrypted within the
+ exchange database.
+
+* **Check**: A check establishes a particular attribute of a user, such as
+ their name based on an ID document and lifeness, mailing address, phone
+ number, taxpayer identity, etc. Checks may be given *context* (such as
+ whether a customer is an individual or a business) to run correctly. Checks
+ can also be AML staff inserting information for plausibilization. Checks
+ result in *attributes* about the account's owner which are given to an
+ external AML *program* together with the *context* to determine an *outcome*.
+ KYC checks are always specified with a fallback *measure* to be taken if
+ the check fails.
+
+* **Condition**: A condition specifies when KYC is required. Conditions
+ include the *type of operation*, a threshold amount (e.g. above EUR:1000)
+ and possibly a time period (e.g. over the last month).
+
+* **Configuration**: The configuration determines the *legitimization rules*,
+ and specifies which providers offer which *checks*.
+
+* **Context**: Context is information provided as input into a *check* and
+ *program* to customize their execution. The context is initially set by the
+ *measure* (possibly including data from the *trigger*). Naturally, the
+ *program* may use its `AmlProgramInput` which includes *context* and
+ *attribute* data to compute an update *context* for the next set of
+ *measures* that it specifies in the `LegitimizationRuleSet` as part
+ of the `AmlOutcome`. Thus, *context* is something that typically
+ evolves as the *account* undergoes *measures*. Context is lost if
+ an account transitions to default *legitimization rules* due to
+ *expiration*.
+
+* **Display priority**: Every rule has a *display priority*. If a second
+ *rule* is *triggered* before the *outcome* of a *rule* could be determined,
+ the *rule* with the larger *display priority* becomes the requirement that
+ the account owner has to satisfy (and that thus will be displayed by the
+ KYC SPA).
+
+* **Expiration**: Except for the default rules, any set of KYC rules is
+ subject to *expiration*. This can be because *attributes* become outdated or
+ because sanctions have a time limit. The expiration time thus determines
+ when a new *measure* is triggered in the absence of a transaction crossing
+ thresholds in the current set of *legtimization rules*.
+
+* **Legitimization rules**: The *legitimization rules* determine under which
+ *conditions* which *measures* will be taken. A `LegitimizationRuleSet`
+ always also includes an *expiration* time period for (custom, non-default)
+ *legitimization rules* after which a fallback measure* will automatically
+ apply. Legitimization rules may be *exposed* to the client (for example,
+ to allow a wallet to stay below hard withdraw thresholds) or could be secret.
+
+* **Logic**: Logic refers to a specific bit of code (realized as an exchange
+ plugin) that enables the interaction with a specific *provider*. Logic
+ typically requires *configuration* for access control (such as an
+ authorization token) and possibly the endpoint of the specific *provider*
+ implementing the respective API.
+
+* **Measure**: Describes the possible outgoing edges from one state in the
+ state machine (including how to show the current state). Each edge is given
+ some *context* and a *check* to be performed as well as an AML *program*
+ which determines the *outcome*. We generally distinguish between
+ "original" measures (defined globally in the exchange configuration) and
+ "custom" measures (defined specifically for an account by AML staff).
+
+* **Outcome**: An `AmlOutcome` describes the account state that an account
+ ends up in due to either an AML staff action or an AML *program* doing some
+ computation over the attributes resulting from a *check*. Outcomes can be
+ that certain types of transactions are "verboten", that the account is (or
+ remains) under investigation by AML staff, that the account is given certain
+ properties, and/or that certain events are to be logged. Outcomes also
+ include a new set of *legitimization rules* to apply (and an *expiration*
+ time at which point a successor *measure* will be automatically taken).
+
+* **Provider**: A provider performs a specific set of *checks* at a certain
+ *cost*. Interaction with a provider is performed by provider-specific
+ *logic*.
+
+* **Program**: An AML helper *program* is given *context* about the current
+ state of an account and the attribute data from a *check* to compute the
+ *outcome*. For example, a *program* may look at the "PEP" field of a KYC
+ check and decide if the outcome is to put the account into ``normal`` or
+ ``held-for-manual-review`` state. AML programs are always specified
+ with a fallback *measure* to be taken if the program fails.
+
+* **Trigger**: A specific transaction that satisfies a **Condition**.
+
+* **Type of operation**: The operation type determines which Taler-specific
+ operation has triggered the KYC requirement. We support four types of
+ operation: withdraw (by customer), deposit (by merchant), P2P receive (by
+ wallet) and (high) wallet balance.
+
+
+Account owner authentication
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Access to the KYC SPA (or rather, its account-specific state) is controlled by
+a *target token* (which is effectively like a bearer token, except passed
+inside the URL). The *target token* ensures that only the account owner has
+access to the KYC processes. It can be obtained by authenticating using
+either the merchant private key or reserve private key, depending on the type
+of the account (IBAN or wallet-reserve respectively).
+
+When we need to authenticate a bank account owner, we will simply require them
+to make an outgoing wire transfer into the exchange bank account with a public
+key in the wire transfer subject (just like when withdrawing), but augmented
+with the string "KYC" so we can distinguish the wire transfer from a regular
+withdrawal. Typically, we would put the merchant public key into the wire
+transfer subject; wallets MAY put their long-term reserve public key instead.
+The amount to be transferred is the *KYC fee*.
+
+This has several advantages:
+
+* Only the account owner can provide us with the public key, so we already
+ have also one super-hard piece of KYC evidence.
+* If the account owner looses their public key, it's not a problem: they
+ would just have to do the transfer again with a new key. No need for
+ us to do any kind of intervention for key management.
+* We could theoretically get paid to do the KYC process, or just "charge" a
+ nominal amount.
+* This also somewhat addresses the payment for voluntary KYC processes where
+ a merchant wants to do KYC to get us to attest their identity for their
+ customers even if we do not yet have a legal need. The only issue here
+ is that this does not work if voluntary KYC is invoiced while mandatory
+ KYC is gratis. But, that kind of configuration is a business decision
+ and there is no hard need to support it immediately.
+* This definitively addresses the need for authentication to access the
+ attestation API, which so far was only available for P2P payments as
+ we could not authenticate merchants.
+* The "KYC" string allows us to distinguish the authentication transfers from
+ withdrawal transfers; by keeping the KYC fee at or below the closing fee,
+ we can even deploy this without fully updating the logic everywhere to
+ distinguish KYC transfers
+
+TODO: update wire gateway specification, update/new tables for KYC wire
+transfers, update API spec for attestation, update exchange API (below) to
+signal need for auth-payment via wire transfer, update merchant logic to
+expose merchant public key to SPA for wire transfer if needed for KYC.
451 Response
^^^^^^^^^^^^
When KYC operations are required, various endpoints may respond with a
-``451 Unavailable for Legal Reasons`` status code and a `KycNeededRedirect`
+``451 Unavailable for Legal Reasons`` status code and a `LegitimziationNeededResponse`
body.
- .. ts:def:: KycNeededRedirect
-
- interface KycNeededRedirect {
-
- // 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;
-
- // Public key associated with the account. The client must sign
- // the initial request for the KYC status using the corresponding
- // private key. Will be either a reserve public key or a merchant
- // (instance) public key.
- account_pub: EddsaPublicKey;
-
- // Legitimization target that the merchant should
- // use to check for its KYC status using
- // the ``/kyc-check/$REQUIREMENT_ROW`` endpoint.
- requirement_row: Integer;
-
- }
-
-
-New Endpoints
+New endpoints
^^^^^^^^^^^^^
.. http:get:: /kyc-check/$REQUIREMENT_ROW
- Checks the KYC status of a particular payment target and possibly begins the
- KYC process. This endpoint is typically 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.
+ Checks the KYC status of a particular payment target and possibly begins a
+ KYC process by allowing the customer to choose the next KYC measure to
+ satisfy. This endpoint is typically used by wallets or merchants that
+ have been told that a transaction is not happening because it triggered
+ some KYC/AML measure and now want to check how the KYC/AML
+ requirement could be fulfilled (or whether it already has been
+ statisfied and the operation can now proceed). Long-polling may be used
+ to instantly observe a change in the KYC requirement status.
The requirement row of the ``/kyc-check/`` endpoint encodes the
legitimization measure's serial number. It is returned in
- `KycNeededRedirect` responses via the ``requirement_row`` field.
+ `LegitimizationNeededResponse` responses via the ``requirement_row`` field.
Given a valid pair of requirement row and account owner signature, the
``/kyc-check/`` endpoint returns either just the KYC status or redirects the
@@ -299,12 +463,14 @@ New Endpoints
**Request:**
- *Account-Owner-Signature*: The client must provide Base-32 encoded EdDSA
- signature with ``$ACCOUNT_PRIV``, affirming the desire to obtain KYC data.
- Note that this is merely a simple authentication mechanism, the details of
- the request are not protected by the signature. The ``$ACCOUNT_PRIV``
- is either the (wallet long-term) reserve private key or the merchant
- instance private key.
+ *Account-Owner-Signature*:
+
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$ACCOUNT_PRIV``, affirming the desire to obtain KYC data. Note that
+ this is merely a simple authentication mechanism, the details of the
+ request are not protected by the signature. The ``$ACCOUNT_PRIV`` is
+ either the (wallet long-term) reserve private key or the merchant instance
+ private key.
:query timeout_ms=NUMBER: *Optional.* If specified, the exchange will
wait up to ``timeout_ms`` milliseconds if the requirement continues
@@ -334,101 +500,26 @@ New Endpoints
The exchange is not configured to perform KYC and thus
the legal requirements are already satisfied.
:http:statuscode:`403 Forbidden`:
- The provided hash does not match the requirement row.
+ The provided signature is not acceptable for the requirement row.
:http:statuscode:`404 Not found`:
The requirement row is unknown.
- **Details:**
-
- .. ts:def:: AccountKycStatus
-
- interface AccountKycStatus {
-
- // Current time of the exchange, used as part of
- // what the exchange signs over.
- now: Timestamp;
-
- // Current AML state for the target account. True if
- // operations are not happening due to staff processing
- // paperwork *or* due to legal requirements (so the
- // client cannot do anything but wait).
- //
- // Note that not every AML staff action may be legally
- // exposed to the client, so this is merely a hint that
- // a client should be told that AML staff is currently
- // reviewing the account. AML staff *may* review
- // accounts without this flag being set!
- aml_review: boolean;
-
- // URL that the user should open in a browser to
- // proceed with the KYC process (optional if
- // the status type is ``200 Ok``, mandatory if the
- // HTTP status is ``202 Accepted``).
- kyc_url: string;
-
- // Array with limitations that currently apply to this
- // account and that may be increased or lifted if the
- // KYC check is passed.
- // Note that additional limits *may* exist and not be
- // communicated to the client. If such limits are
- // reached, this *may* be indicated by the account
- // going into ``aml_review`` state. However, it is
- // also possible that the exchange may legally have
- // to deny operations without being allowed to provide
- // any justification.
- // The limits should be used by the client to
- // possibly structure their operations (e.g. withdraw
- // what is possible below the limit, ask the user to
- // pass KYC checks or withdraw the rest after the time
- // limit is passed, warn the user to not withdraw too
- // much or even prevent the user from generating a
- // request that would cause it to exceed hard limits).
- limits?: AccountLimit[];
-
- }
-
- .. ts:def:: AccountLimit
-
- interface AccountLimit {
-
- // Operation that is limited.
- // Must be one of "WITHDRAW", "DEPOSIT", "P2P-RECEIVE"
- // or "WALLET-BALANCE".
- operation_type: string;
-
- // Timeframe during which the limit applies.
- timeframe: RelativeTime;
-
- // Maximum amount allowed during the given timeframe.
- // Zero if the operation is simply forbidden.
- threshold: Amount;
-
- // True if this is a soft limit that could be raised
- // by passing KYC checks. Clients *may* deliberately
- // try to cross limits and trigger measures resulting
- // in 451 responses to begin KYC processes.
- // Clients that are aware of hard limits *should*
- // inform users about the hard limit and prevent flows
- // in the UI that would cause violations of hard limits.
- soft_limit: boolean;
- }
-
-
-.. http:get:: /kyc-spa/$TARGET_TOKEN
+.. http:get:: /kyc-spa/$ACCESS_TOKEN
.. http:get:: /kyc-spa/$FILENAME
- A set of ``/kyc-spa/$TARGET_TOKEN`` GET endpoints is created per account
+ A set of ``/kyc-spa/$ACCESS_TOKEN`` GET endpoints is created per account
hash that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint
will in principle redirect clients. The KYC SPA will use the
- ``$TARGET_TOKEN`` of its URL to initialize itself via the
- ``/kyc-info/$TARGET_TOKEN`` endpoint family. The KYC SPA may download
+ ``$ACCESS_TOKEN`` of its URL to initialize itself via the
+ ``/kyc-info/$ACCESS_TOKEN`` endpoint family. The KYC SPA may download
additional resources via ``/kyc-spa/$FILENAME``. The filenames must not
match base32-encoded 256-bit values.
-.. http:get:: /kyc-info/$TARGET_TOKEN
+.. http:get:: /kyc-info/$ACCESS_TOKEN
- A new set of ``/kyc-info/$TARGET_TOKEN`` GET endpoints is created per client
- account hash to return information about the state of the KYC or AML process
+ The ``/kyc-info/$ACCESS_TOKEN`` endpoints are created per client
+ account hash (but access controlled via a unique target token)
+ to return information about the state of the KYC or AML process
to the KYC SPA. The SPA uses this information to show the user an
appropriate dialog. The SPA should also long-poll this endpoint for changes
to the AML/KYC state. Note that this is a client-facing endpoint, so it will
@@ -441,86 +532,25 @@ New Endpoints
provider, the SPA must request the respective process to be set-up by the
exchange via the ``/kyc-start/`` endpoint.
- **Request**:
+ **Request:**
+
+ *If-None-Match*:
+ The client MAY provide an ``If-None-Match`` header with an ETag.
- *If-None-Match*: The client MAY provide an ``If-None-Match`` header with
- an ETag.
+ :query timeout_ms=MILLISECONDS:
+ *Optional.* If specified, the exchange will wait up to MILLISECONDS for
+ a change to a more recent legitimization measure before returning a 304
+ Not Modified status.
- :query timeout_ms=MILLISECONDS: *Optional.* If specified, the exchange
- will wait up to MILLISECONDS for a change to a more recent legitimization
- measure before returning a 304 Not Modified status.
+ **Response:**
- **Response**:
+ *Etag*: Will be set to the serial ID of the measure. Used for long-polling (only for 200 OK responses).
:http:statuscode:`200 OK`:
The body is a `KycProcessClientInformation`.
-
- *Etag*: Will be set to the serial ID of the measure. Used for long-polling.
-
- .. ts:def:: KycProcessClientInformation
-
- interface KycProcessClientInformation {
-
- // List of requirements.
- requirements?: { name : KycRequirementInformation};
-
- // True if the client is expected to eventually satisfy all requirements.
- // Default (if missing) is false.
- is_and_combinator?: boolean
-
- // List of available voluntary checks the client could pay for.
- // Since **vATTEST**.
- voluntary_checks?: { name : KycCheckInformation};
- }
-
- .. ts:def:: KycRequirementInformation
-
- interface KycRequirementInformation {
-
- // Which form should be used? Common values include "INFO"
- // (to just show the descriptions but allow no action),
- // "LINK" (to enable the user to obtain a link via
- // ``/kyc-start/``) or any build-in form name supported
- // by the SPA.
- form: string;
-
- // English description of the requirement.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized
- // description texts.
- description_i18n ?: { [lang_tag: string]: string };
-
- // ID of the requirement, useful to construct the
- // ``/kyc-upload/$ID`` or ``/kyc-start/$ID`` endpoint URLs.
- // Present if and only if "form" is not "INFO". The
- // ``$ID`` value may itself contain ``/`` or ``?`` and
- // basically encode any URL path (and optional arguments).
- id?: string;
-
- }
-
- .. ts:def:: KycCheckInformation
-
- // Since **vATTEST**.
- interface KycCheckInformation {
-
- // How much would this check cost the client?
- cost: Amount;
-
- // English description of the check.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized
- // description texts.
- description_i18n ?: { [lang_tag: string]: string };
-
- }
-
:http:statuscode:`204 No Content`:
There are no open KYC requirements or possible voluntary checks
the client might perform.
-
:http:statuscode:`304 Not Modified`:
The KYC requirements did not change.
@@ -530,16 +560,16 @@ New Endpoints
The ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload
client-provided evidence. The ``$ID`` will be provided as part of the
``/kyc-info`` body. This is for checks of type ``FORM``. In practice,
- ``$ID`` will encode both the ``$TARGET_TOKEN`` and the index of the selected
+ ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of the selected
measure (but this should be irrelevant for the client).
- **Request**:
+ **Request:**
Basically oriented along the possible formats of a HTTP form being
POSTed. Details will depend on the form. The server will try to decode the
uploaded body from whatever format it is provided in.
- **Response**:
+ **Response:**
:http:statuscode:`204 No Content`:
The information was successfully uploaded. The SPA should fetch
@@ -548,7 +578,7 @@ New Endpoints
The ``$ID`` is unknown to the exchange.
:http:statuscode:`409 Conflict`:
The upload conflicts with a previous upload.
- :http:statuscode:`413 Content Too Large`:
+ :http:statuscode:`413 Request Entity Too Large`:
The body is too large.
.. http:post:: /kyc-start/$ID
@@ -557,27 +587,19 @@ New Endpoints
KYC process. It will return the URL that the client must GET to begin the
KYC process. The SPA should probably open this URL in a new window or tab.
The ``$ID`` will be provided as part of the ``/kyc-info`` body. In
- practice, ``$ID`` will encode both the ``$TARGET_TOKEN`` and the index of
+ practice, ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of
the selected measure (but this should be irrelevant for the client).
- **Request**:
+ **Request:**
Use empty JSON body for now.
- **Response**:
+ **Response:**
:http:statuscode:`200 Ok`:
The KYC process was successfully initiated. The URL is in a
`KycProcessStartInformation` object.
- .. ts:def:: KycProcessStartInformation
-
- interface KycProcessStartInformation {
-
- // URL to open.
- redirect_url: string;
- }
-
:http:statuscode:`404 Not Found`:
The ``$ID`` is unknown to the exchange.
@@ -595,9 +617,9 @@ New Endpoints
``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION`` endpoint. Once this endpoint is
triggered, the exchange will pass the received arguments to the respective
logic plugin. The logic plugin will then (asynchronously) update the KYC
- status of the user. The logic plugin should return a human-readable HTML
- page with the KYC result to the user. This endpoint deliberately does
- not use the ``$TARGET_TOKEN`` as the provider should not learn that token.
+ status of the user. The logic plugin should redirect the user to the KYC
+ SPA. This endpoint deliberately does not use the ``$ACCESS_TOKEN`` as the
+ external KYC provider should not learn that token.
This endpoint is thus accessed from the user's browser at the *end* of a KYC
process, possibly providing the exchange with additional credentials to
@@ -616,8 +638,10 @@ New Endpoints
If the KYC plugin logic is OAuth 2.0, the query parameters are:
- :query code=CODE: OAuth 2.0 code argument.
- :query state=STATE: OAuth 2.0 state argument with the H_PAYTO.
+ :query code=CODE:
+ OAuth 2.0 code argument.
+ :query state=STATE:
+ OAuth 2.0 state argument with the H_PAYTO.
.. note::
@@ -674,7 +698,7 @@ New Endpoints
The specified logic is unknown.
-.. http:post:: /kyc-wallet``
+.. http:post:: /kyc-wallet
The ``/wallet-kyc`` POST endpoint allows a wallet to notify an exchange if
it will cross a balance threshold. Here, the ``balance`` specified should be
@@ -708,42 +732,7 @@ New Endpoints
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:**
-
- .. ts:def:: WalletKycRequest
-
- 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``
- reserve_sig: EddsaSignature;
-
- // long-term wallet reserve-account
- // public key used to create the signature.
- reserve_pub: EddsaPublicKey;
- }
-
- .. ts:def:: WalletKycUuid
-
- interface WalletKycUuid {
-
- // UUID that the wallet should use when initiating
- // the KYC check.
- requirement_row: number;
-
- // Hash of the payto:// account URI for the wallet.
- h_payto: PaytoHash;
-
- }
-
+ The response will be a `LegitimizationNeededResponse` object.
.. http:get:: /aml/$OFFICER_PUB/measures
@@ -753,125 +742,148 @@ New Endpoints
KYC checks (by name) with their descriptions and a list of AML programs with
information about the required context.
- **Request**:
+ **Request:**
- *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.
+ *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**:
+ **Response:**
:http:statuscode:`200 Ok`:
Information about possible measures is returned in a
`AvailableMeasureSummary` object.
- .. ts:def:: AvailableMeasureSummary
-
- interface AvailableMeasureSummary {
-
- // Available original measures that can be
- // triggered directly by default rules.
- roots: { "$measure_name" : MeasureInformation };
-
- // Available AML programs.
- programs: { "$prog_name" : AmlProgramRequirement };
-
- // Available KYC checks.
- checks: { "$check_name" : KycCheckInformation };
-
- }
-
- .. ts:def:: MeasureInformation
-
- interface MeasureInformation {
-
- // Name of a KYC check.
- check_name: string;
-
- // Name of an AML program.
- prog_name: string;
+.. http:get:: /aml/$OFFICER_PUB/kyc-statistics/$NAME
- // Context for the check. Optional.
- context?: Object;
+ Returns the number of KYC events matching the given event type ``$NAME`` in
+ the specified time range. Note that this query can be slow as the
+ statistics are computed on-demand. (This is OK as such requests should be
+ rare.)
- }
+ **Request:**
- .. ts:def:: AmlProgramRequirement
+ *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.
- interface AmlProgramRequirement {
+ :query start_date=TIMESTAMP:
+ *Optional*. Specifies the date when to
+ start looking (inclusive). If not given, the start time of the
+ exchange operation is used.
+ :query end_date=TIMESTAMP:
+ *Optional*. Specifies the date when to
+ stop looking (exclusive). If not given, the current date is used.
- // Description of what the AML program does.
- description: string;
+ **Response:**
- // List of required field names in the context to run this
- // AML program. SPA must check that the AML staff is providing
- // adequate CONTEXT when defining a measure using this program.
- context: string[];
+ :http:statuscode:`200 OK`:
+ The responds will be an `EventCounter` message.
- // List of required attribute names in the
- // input of this AML program. These attributes
- // are the minimum that the check must produce
- // (it may produce more).
- inputs: string[];
+.. http:get:: /aml/$OFFICER_PUB/decisions
- }
+ **Request:**
- .. ts:def:: KycCheckInformation
+ *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.
+
+ :query limit:
+ *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 offset:
+ *Optional*. Row number threshold, see ``delta`` for its
+ interpretation. Defaults to ``INT64_MAX``, namely the biggest row id
+ possible in the database.
+ :query h_payto:
+ *Optional*. Account selector. All matching accounts are returned if this
+ filter is absent, otherwise only decisions for this account.
+ :query active:
+ *Optional*. If set to yes, only return active decisions, if no only
+ decisions that have been superceeded. Do not give (or use "all") to
+ see all decisions regardless of activity status.
+ :query investigation:
+ *Optional*. If set to yes, only return accounts that are under
+ AML investigation, if no only accounts that are not under investigation.
+ Do not give (or use "all") to see all accounts regardless of
+ investigation status.
- interface KycCheckInformation {
+ **Response:**
- // Description of the KYC check. Should be shown
- // to the AML staff but will also be shown to the
- // client when they initiate the check in the KYC SPA.
- description: string;
- description_i18n: {};
+ :http:statuscode:`200 OK`:
+ The responds will be an `AmlDecisions` 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.
- // Names of the fields that the CONTEXT must provide
- // as inputs to this check.
- // SPA must check that the AML staff is providing
- // adequate CONTEXT when defining a measure using
- // this check.
- requires: string[];
+.. http:get:: /aml/$OFFICER_PUB/attributes/$H_PAYTO
- // Names of the attributes the check will output.
- // SPA must check that the outputs match the
- // required inputs when combining a KYC check
- // with an AML program into a measure.
- outputs: string[];
+ Obtain attributes obtained as part of AML/KYC processes for a
+ given account.
- // Name of a root measure taken when this check fails.
- fallback: string;
- }
+ **Request:**
-.. http:get:: /aml/kyc-statistics/$NAME
+ *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.
+
+ :query limit:
+ *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 offset:
+ *Optional*. Row number threshold, see ``delta`` for its
+ interpretation. Defaults to ``INT64_MAX``, namely the biggest row id
+ possible in the database.
- Returns the number of KYC events matching the given
- event type ``$NAME`` in the specified time range.
- Note that this query can be slow as the statistics
- are computed on-demand. (This is OK as such requests
- should be rare.)
+ **Response:**
- **Request**:
+ :http:statuscode:`200 OK`:
+ The responds will be an `KycAttributes` message.
+ :http:statuscode:`204 No content`:
+ There are no matching KYC attributes.
+ :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.
- *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.
+.. http:post:: /aml/$OFFICER_PUB/decision
- :query start_date=TIMESTAMP: *Optional*. Specifies the date when to start looking (inclusive). If not given, the start time of the exchange operation is used.
- :query end_date=TIMESTAMP: *Optional*. Specifies the date when to stop looking (exclusive). If not given, the current date is used.
+ Make an AML decision. Triggers the respective action and
+ records the justification.
- **Response**:
+ **Request:**
- .. ts:def:: EventCounter
+ The request body must be an `AmlDecision` message.
- interface EventCounter {
- // Number of events of the specified type in
- // the given range.
- cnt: Integer;
- }
+ **Response:**
+ :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.
Modifications to existing endpoints
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -917,35 +929,35 @@ specifies a ``$PROVIDER_SECTION`` for each authentication procedure. For each
determines the redirect URL for a given wire target. See below for a
description of the high-level process for different providers.
- .. code-block:: ini
+.. code-block:: ini
- [kyc-provider-$PROVIDER_ID]
+ [kyc-provider-$PROVIDER_ID]
- # Which plugin is responsible for this provider?
- LOGIC = PLUGIN_NAME
+ # Which plugin is responsible for this provider?
+ LOGIC = PLUGIN_NAME
- # Optional cost, useful if clients want to voluntarily
- # trigger authentication procedures for attestation.
- # Since **vATTEST**.
- COST = EUR:5
+ # Name of a program to run on the output of the plugin
+ # to convert the result into the desired set of attributes.
+ # The converter must create a log for the system administrator
+ # if the provided inputs do not match expectations.
+ # Note that the converter will be expected to output the
+ # set of attributes listed under the respective ``[kyc-check-*]``
+ # sections. Calling the converter with ``--list-outputs``
+ # should generate a (newline-separated) list of attributes
+ # the converter promises to generate in its JSON output
+ # (when run regularly).
+ CONVERTER = taler-exchange-helper-$NAME
- # Plus additional logic-specific options, e.g.:
- AUTHORIZATION_TOKEN = superdupersecret
+ # Optional cost, useful if clients want to voluntarily
+ # trigger authentication procedures for attestation.
+ # Since **vATTEST**.
+ COST = EUR:5
- # Other logic-specific internal options (example):
- FORM_ID = business_legi_form
+ # Plus additional logic-specific options, e.g.:
+ AUTHORIZATION_TOKEN = superdupersecret
- # Name of a program to run on the output of the plugin
- # to convert the result into the desired set of attributes.
- # The converter must create a log for the system administrator
- # if the provided inputs do not match expectations.
- # Note that the converter will be expected to output the
- # set of attributes listed under the respective ``[kyc-check-*]``
- # sections. Calling the converter with ``--list-outputs``
- # should generate a (newline-separated) list of attributes
- # the converter promises to generate in its JSON output
- # (when run regularly).
- CONVERTER = taler-exchange-helper-$NAME
+ # Other logic-specific internal options (example):
+ FORM_ID = business_legi_form
Configuration of possible KYC/AML checks
@@ -954,68 +966,72 @@ Configuration of possible KYC/AML checks
The configuration specifies a set of possible KYC checks offered by external
providers, one per configuration section:
- .. code-block:: ini
-
- [kyc-check-$CHECK_NAME]
-
- # Which type of check is this? Also determines
- # the SPA form to show to the user for this check.
- #
- # INFO: wait for staff or contact staff out-of band
- # (only information shown, no SPA action)
- # FORM: SPA should show an inline (HTML) form
- # LINK: SPA may start external KYC process or upload
- #
- TYPE = INFO|LINK|FORM
-
- # Optional. Set to YES to allow this check be
- # done voluntarily by a client (they may then
- # still have to pay for it). Used to offer the
- # SPA to display checks even if they are
- # not required. Default is NO.
- # Since **vATTEST**.
- VOLUNTARY = YES/NO
-
- # Provider id, present only if type is LINK.
- PROVIDER_ID = id
-
- # Name of the SPA form, if type is FORM
- # "INFO" and "LINK" are reserved and must not be used.
- # The exchange server and the SPA must agree on a list
- # of supported forms and the resulting attributes.
- #
- # The SPA should include a JSON resource file
- # "forms.json" mapping form names to arrays of
- # attribute names each form provides.
- FORM_NAME = name
-
- # Descriptions to use in the SPA to display the check.
- DESCRIPTION = "Upload your passport picture"
- DESCRIPTION_I18N = "{"en":"Upload scan of your passport"}"
-
- # ';'-separated list of fields that the CONTEXT must
- # provided as inputs to this check. For example,
- # for a FORM of type CHOICE, this might state
- # ``choices: string[];``. The type after the ":"
- # is for now purely for documentation and is
- # not checked. However, it may be shown to AML staff
- # when they configure measures.
- REQUIRES = requirement;
-
- # Description of the outputs provided by the check.
- # Basically, the check's output is expected to
- # provide the following fields as inputs into
- # a subsequent AML program.
- OUTPUTS = business_name street city country registration
-
- # **original** measure to take if the check fails
- # (for any reason, e.g. provider or form fail to
- # satisfy constraints or provider signals user error)
- # Usually should point to a measure that requests
- # AML staff to investigate. The fallback measure
- # context always includes the reasons for the
- # failure.
- FALLBACK = MEASURE_NAME
+.. code-block:: ini
+
+ [kyc-check-$CHECK_NAME]
+
+ # Which type of check is this? Also determines
+ # the SPA form to show to the user for this check.
+ #
+ # INFO: wait for staff or contact staff out-of band
+ # (only information shown, no SPA action)
+ # FORM: SPA should show an inline (HTML) form
+ # LINK: SPA may start external KYC process or upload
+ #
+ TYPE = INFO|LINK|FORM
+
+ # Optional. Set to YES to allow this check be
+ # done voluntarily by a client (they may then
+ # still have to pay for it). Used to offer the
+ # SPA to display checks even if they are
+ # not required. Default is NO.
+ # Since **vATTEST**.
+ VOLUNTARY = YES/NO
+
+ # Provider id, present only if type is LINK.
+ # Refers to a ``kyc-provider-$PROVIDER_ID`` section.
+ PROVIDER_ID = id
+
+ # Name of the SPA form, if type is FORM
+ # "INFO" and "LINK" are reserved and must not be used.
+ # The exchange server and the SPA must agree on a list
+ # of supported forms and the resulting attributes.
+ #
+ # The SPA should include a JSON resource file
+ # "forms.json" mapping form names to arrays of
+ # attribute names each form provides.
+ FORM_NAME = name
+
+ # Descriptions to use in the SPA to display the check.
+ DESCRIPTION = "Upload your passport picture"
+ DESCRIPTION_I18N = "{"en":"Upload scan of your passport"}"
+
+ # ';'-separated list of fields that the CONTEXT must
+ # provide as inputs to this check. For example,
+ # for a FORM of type CHOICE, this might state
+ # ``choices: string[];``. The type after the ":"
+ # is for now purely for documentation and is
+ # not checked. However, it may be shown to AML staff
+ # when they configure measures.
+ REQUIRES = requirement;
+
+ # Description of the outputs provided by the check.
+ # Basically, the check's output is expected to
+ # provide the following fields as attribute inputs into
+ # a subsequent AML program.
+ # Only given for type FORM; INFO never has any outputs,
+ # and for type LINK we can obtain the same information
+ # from the CONVERTER via ``--list-outputs``.
+ OUTPUTS = business_name street city country registration
+
+ # **original** measure to take if the check fails
+ # (for any reason, e.g. provider or form fail to
+ # satisfy constraints or provider signals user error)
+ # Usually should point to a measure that requests
+ # AML staff to investigate. The fallback measure
+ # context always includes the reasons for the
+ # failure.
+ FALLBACK = MEASURE_NAME
The list of possible FORM names is fixed in the SPA
for a particular exchange release.
@@ -1031,52 +1047,50 @@ The configuration also specifies a set of legitimization rules including the
condition and the measure the condition triggers, one condition per
configuration section:
- .. code-block:: ini
-
- [kyc-rule-$RULE_NAME]
+.. code-block:: ini
- # Operation that triggers this legitimization.
- # Must be one of WITHDRAW, DEPOSIT, P2P-RECEIVE
- # or WALLET-BALANCE.
- OPERATION_TYPE = WITHDRAW
+ [kyc-rule-$RULE_NAME]
- # Next measures to be performed. The SPA should
- # display *all* of these measures to the user.
- # (they have a choice of either which ones, or in
- # which order they are to be performed).
- # A special measure "verboten" is used if the
- # threshold may never be crossed.
- NEXT_MEASURES = SWISSNESS KYB
+ # Operation that triggers this rule.
+ # Must be one of WITHDRAW, DEPOSIT, P2P-RECEIVE
+ # or WALLET-BALANCE.
+ OPERATION_TYPE = WITHDRAW
- # Context for each of the above measures, optional.
- MEASURE_CONTEXT_$NAME = CONTEXT
+ # Space-separated list of next measures to be performed.
+ # The SPA should display *all* of these measures to the user.
+ # (They have a choice of either which ones, or in
+ # which order they are to be performed.)
+ # A special measure name "verboten" is used if the
+ # specified threshold may never be crossed
+ # (under this set of rules).
+ NEXT_MEASURES = SWISSNESS KYB
- # "yes" if all REQUIRED_MEASURES will eventually need
- # to be satisfied, "no" if the user has a choice between
- # them. Not actually enforced by the exchange, but
- # primarily used to inform the user whether this is
- # an "and" or "or". YES for "and".
- IS_AND_COMBINATOR = YES
+ # "YES" if all NEXT_MEASURES will eventually need
+ # to be satisfied, "NO" if the user has a choice between
+ # them. Not actually enforced by the exchange, but
+ # primarily used to inform the user whether this is
+ # an "and" or "or". YES for "and".
+ IS_AND_COMBINATOR = YES
- # YES if the rule (specifically, operation type,
- # threshold, timeframe) and the general nature of
- # the next measure (verboten or approval required)
- # should be exposed to the client.
- # Defaults to NO if not set.
- EXPOSED = YES
+ # YES if the rule (specifically, operation type,
+ # threshold, timeframe) and the general nature of
+ # the next measure (verboten or approval required)
+ # should be exposed to the client.
+ # Defaults to NO if not set.
+ EXPOSED = YES
- # Threshold amount above which the legitimization is
- # triggered. The total must be exceeded in the given
- # timeframe. Can be 'forever'.
- THRESHOLD = AMOUNT
+ # Threshold amount above which the rule is
+ # triggered. The total must be exceeded in the given
+ # timeframe.
+ THRESHOLD = KUDOS:100
- # Timeframe over which the amount to be compared to
- # the THRESHOLD is calculated.
- # Ignored for WALLET-BALANCE.
- TIMEFRAME = DURATION
+ # Timeframe over which the amount to be compared to
+ # the THRESHOLD is calculated.
+ # Ignored for WALLET-BALANCE. Can be 'forever'.
+ TIMEFRAME = 30 days
- # Enabled (default is NO)
- ENABLED = NO
+ # Set to YES to enable the rule (default is NO)
+ ENABLED = NO
AML programs
@@ -1098,128 +1112,10 @@ AML programs are helper programs that can:
(but may also include FORM field names).
* Process an input JSON object of type
`AmlProgramInput` into a JSON object of
- type `AmlProgramOutcome`.
+ type `AmlOutcome`.
This is the default behavior if no command-line switches
are provided.
-.. ts:def:: AmlProgramInput
-
- interface AmlProgramInput {
-
- // JSON object that was provided as
- // part of the *measure*. This JSON object is
- // provided under "context" in the main JSON object
- // input to the AML program. This "context" should
- // satify both the REQUIRES clause of the respective
- // check and the output of "--requires" from the
- // AML program's command-line option.
- context?: Object;
-
- // JSON object that captures the
- // output of a ``[kyc-provider-]`` or (HTML) FORM.
- // The keys in the JSON object will be the attribute
- // names and the values must be strings representing
- // the data. In the case of file uploads, the data
- // MUST be base64-encoded.
- attributes: Object;
-
- // JSON array with the results of historic
- // AML desisions about the account.
- // FIXME: review AmlDecisionDetail spec!
- // (need to enable new outcomes!)
- aml_history: AmlDecisionDetail[];
-
- // JSON array with the results of historic
- // KYC data about the account.
- // FIXME: review KycDetail spec!
- // (need to include AmlProgramOutcome!)
- kyc_history: KycDetail[];
-
- }
-
-.. ts:def:: AmlProgramOutcome
-
- interface AmlProgramOutcome {
-
- // Should the client's account be investigated
- // by AML staff?
- // Defaults to false.
- to_investigate?: boolean;
-
- // Should the client's account be frozen?
- // Defaults to false.
- is_frozen?: boolean;
-
- // Was the client's account reported to the authorities?
- // Defaults to false.
- is_reported?: boolean;
-
- // Free-form properties about the account.
- // Can be used to store properties such as PEP,
- // risk category, type of business, hits on
- // sanctions lists, etc.
- properties?: AccountProperties;
-
- // Types of events to add to the KYC events table.
- // (for statistics).
- events?: string[];
-
- // When does the outcome expire?
- expiration_time: Timestamp;
-
- // Name of the measure to apply the outcome expires.
- // If not set, we revert to the default set
- // of rules (and the default account state).
- successor_measure?: string;
-
- // Array of KYC rules to apply. Note that this
- // array overrides *all* of the default rules.
- // Thus, if the array does not have an entry for
- // a particular operation, there would be
- // no thresholds for that operation!
- rules: KycRule[];
-
- // Custom measures that KYC rules may refer to.
- custom_measures?: { "name" : MeasureInformation };
-
- }
-
-.. ts:def:: KycRule
-
- interface KycRule {
-
- // Type of operation to which the rule applies.
- operation_type: string;
-
- // Measure to be taken if the given
- // threshold is crossed over the given timeframe.
- threshold: Amount;
-
- // Over which duration should the threshold be
- // computed.
- timeframe: RelativeTime;
-
- // Array of names of measures to apply.
- // Names listed can be original measures or
- // custom measures from the `AmlProgramOutcome`.
- // A special measure "verboten" is used if the
- // threshold may never be crossed.
- measures: string[];
-
- // True if the rule (specifically, operation_type,
- // threshold, timeframe) and the general nature of
- // the measures (verboten or approval required)
- // should be exposed to the client.
- // Defaults to "false" if not set.
- exposed?: boolean;
-
- // True if all the measures will eventually need to
- // be satisfied, false if any of the measures should
- // do.
- // Default (if missing) is false.
- is_and_combinator?: boolean;
- }
-
If the AML program fails (exits with a failure code or
does not provide well-formed JSON output) the AML/KYC
process continues with the FALLBACK measure. This should
@@ -1228,29 +1124,29 @@ systems administrator.
AML programs are listed in the configuration file, one program per section:
- .. code-block:: ini
+.. code-block:: ini
- [aml-program-$PROG_NAME]
+ [aml-program-$PROG_NAME]
- # Program to run.
- COMMAND = taler-helper-aml-pep
+ # Program to run.
+ COMMAND = taler-helper-aml-pep
- # Human-readable description of what this
- # AML helper program will do. Used to show
- # to the AML staff.
- DESCRIPTION = "check if the customer is a PEP"
+ # Human-readable description of what this
+ # AML helper program will do. Used to show
+ # to the AML staff.
+ DESCRIPTION = "check if the customer is a PEP"
- # True if this AML program is enabled (and thus can be
- # used in measures and exposed to AML staff).
- # Optional, default is NO.
- ENABLED = YES
+ # True if this AML program is enabled (and thus can be
+ # used in measures and exposed to AML staff).
+ # Optional, default is NO.
+ ENABLED = YES
- # **original** measure to take if COMMAND fails
- # Usually points to a measure that asks AML staff
- # to contact the systems administrator. The fallback measure
- # context always includes the reasons for the
- # failure.
- FALLBACK = MEASURE_NAME
+ # **original** measure to take if COMMAND fails
+ # Usually points to a measure that asks AML staff
+ # to contact the systems administrator. The fallback measure
+ # context always includes the reasons for the
+ # failure.
+ FALLBACK = MEASURE_NAME
Configuration of measures
@@ -1259,22 +1155,23 @@ Configuration of measures
Finally, the configuration specifies a set of
**original** *measures* one per configuration section:
- .. code-block:: ini
+.. code-block:: ini
- [kyc-measure-$MEASURE_NAME]
+ [kyc-measure-$MEASURE_NAME]
- # Possible check for this measure. Optional.
- # If not given, PROGRAM should be run immediately
- # (on an empty set of attributes).
- CHECK_NAME = IB_FORM
+ # Possible check for this measure. Optional.
+ # If not given, PROGRAM should be run immediately
+ # (on an empty set of attributes).
+ CHECK_NAME = IB_FORM
- # Context for the check. The context can be
- # just an empty JSON object if there is none.
- CONTEXT = {"choices":["individual","business"]}
+ # Context for the check. The context can be
+ # just an empty JSON object if there is none.
+ CONTEXT = {"choices":["individual","business"]}
- # Program to run on the context and check data to
- # determine the outcome and next measure.
- PROGRAM = taler-aml-program
+ # Program name to run on the context and check data to
+ # determine the outcome and next measure.
+ # Refers to a ``[aml-program-$PROG_NAME]`` section name.
+ PROGRAM = taler-aml-program
If no ``CHECK_NAME`` is provided at all, the AML ``PROGRAM`` is to be run
immediately. This is useful if no client-interaction is required to arrive at
@@ -1335,59 +1232,61 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
CREATE TABLE wire_targets
(wire_target_serial_id BIGSERIAL UNIQUE
- ,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64),
- ,target_token BYTEA UNIQUE CHECK (LENGTH(target_token)=32)
- ,target_pub BYTEA CHECK (LENGTH(target_pub)=32)
+ ,wire_target_h_payto BYTEA PRIMARY KEY CHECK (LENGTH(wire_target_h_payto)=32),
+ ,access_token BYTEA UNIQUE CHECK (LENGTH(access_token)=32) DEFAULT gen_random_bytes(32)
+ ,target_pub BYTEA CHECK (LENGTH(target_pub)=32) DEFAULT NULL
,payto_uri STRING NOT NULL
- ,PRIMARY KEY (h_payto,target_pub)
)
- PARTITION BY HASH (h_payto);
+ PARTITION BY HASH (wire_target_h_payto);
COMMENT ON TABLE wire_targets
IS 'All recipients of money via the exchange';
- COMMENT ON COLUMN wire_targets.payto_uri
- IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
COMMENT ON COLUMN wire_targets.h_payto
IS 'Unsalted hash of payto_uri';
- COMMENT ON COLUMN wire_targets.target_token
- IS 'high-entropy random value that uniquely identifies the wire target and is used as a token to authorize access to the KYC process (without requiring a signature by target_priv); NULL if KYC is not allowed for the account (legacy)';
+ COMMENT ON COLUMN wire_targets.access_token
+ IS 'high-entropy random value that is used as a token to authorize access to the KYC process (without requiring a signature by target_priv)';
COMMENT ON COLUMN wire_targets.target_pub
- IS 'Public key (reserve_pub or merchant_pub) associated with the account; NULL if KYC is not allowed for the account (legacy)';
+ IS 'Public key (reserve_pub or merchant_pub) associated with the account; NULL if KYC is not allowed for the account (if there was no incoming KYC wire transfer yet); updated, thus NOT available to the auditor';
+ COMMENT ON COLUMN wire_targets.payto_uri
+ IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
CREATE TABLE IF NOT EXISTS legitimization_measures
- (legitimization_measure_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
- ,target_token BYTEA NOT NULL UNIQUE CHECK (LENGTH(target_token)=32)
- REFERENCES wire_targets (target_token)
+ (legitimization_measure_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY
+ ,access_token BYTEA NOT NULL UNIQUE CHECK (LENGTH(access_token)=32)
+ REFERENCES wire_targets (access_token)
,start_time INT8 NOT NULL
- ,jmeasures VARCHAR[] NOT NULL
+ ,jmeasures TEXT NOT NULL
+ ,display_priority INT4 NOT NULL
,is_finished BOOL NOT NULL DEFAULT(FALSE)
)
- PARTITION BY HASH (h_payto);
+ PARTITION BY HASH (access_token);
- COMMENT ON COLUMN legitimization_requirements.target_token
+ COMMENT ON TABLE legitimization_measures
+ IS 'Rules that have been triggered for the account (FIXME: check this is consistent with usage)';
+ COMMENT ON COLUMN legitimization_measures.access_token
IS 'Used to uniquely identify the account and as a symmetric access control mechanism for the SPA';
- COMMENT ON COLUMN legitimization_requirements.start_time
+ COMMENT ON COLUMN legitimization_measures.start_time
IS 'Time when the measure was triggered (by decision or rule)';
- COMMENT ON COLUMN legitimization_requirements.jmeasures
+ COMMENT ON COLUMN legitimization_measures.jmeasures
IS 'JSON object of type LegitimizationMeasures with KYC/AML measures for the account encoded';
- COMMENT ON COLUMN legitimization_requirements.is_finished
+ COMMENT ON COLUMN legitimization_measures.display_priority
+ IS 'Display priority of the rule that triggered this measure; if in the meantime another rule also triggers, the measure is only replaced if the new rule has a higher display priority';
+ COMMENT ON COLUMN legitimization_measures.is_finished
IS 'Set to TRUE if this set of measures was processed; used to avoid indexing measures that are done';
- CREATE INDEX ON legitimization_measures (target_token)
- WHERE NOT finished;
+ CREATE INDEX ON legitimization_measures (access_token)
+ WHERE NOT is_finished;
CREATE TABLE legitimization_outcomes
- (outcome_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+ (outcome_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY
,h_payto BYTEA CHECK (LENGTH(h_payto)=32)
- REFERENCES wire_targets (h_payto)
+ REFERENCES wire_targets (wire_target_h_payto)
,decision_time INT8 NOT NULL DEFAULT(0)
,expiration_time INT8 NOT NULL DEFAULT(0)
,jproperties TEXT,
,to_investigate BOOL NOT NULL
- ,is_frozen BOOL NOT NULL
- ,is_reported BOOL NOT NULL
,is_active BOOL NOT NULL DEFAULT(TRUE)
- ,jnew_rules NOT NULL TEXT
+ ,jnew_rules TEXT NOT NULL
)
PARTITION BY HASH (h_payto);
@@ -1398,71 +1297,67 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
COMMENT ON COLUMN legitimization_outcomes.decision_time
IS 'when was this outcome decided';
COMMENT ON COLUMN legitimization_outcomes.expiration_time
- IS 'time when the decision expires and the expiration new_rules should be applied';
+ IS 'time when the decision expires and the expiration jnew_rules should be applied';
COMMENT ON COLUMN legitimization_outcomes.jproperties
IS 'JSON object of type AccountProperties, such as PEP status, business domain, risk assessment, etc.';
COMMENT ON COLUMN legitimization_outcomes.to_investigate
IS 'AML staff should investigate the activity of this account';
- COMMENT ON COLUMN legitimization_outcomes.is_frozen
- IS 'Transactions with this account should be held (until expiration data or AML staff action)';
- COMMENT ON COLUMN legitimization_outcomes.is_reported
- IS 'Set to TRUE if the activity of the account was reported to authorities';
COMMENT ON COLUMN legitimization_outcomes.is_active
IS 'TRUE if this is the current authoritative legitimization outcome';
COMMENT ON COLUMN legitimization_outcomes.jnew_rules
- IS 'JSON object of type LegitimizationRules with rules to apply to the various operation types for this account; all KYC checks should first check if active new rules for a given account exist in this table (and apply specified measures); if not, it should check the default rules to decide if a measure is required';
+ IS 'JSON object of type LegitimizationRuleSet with rules to apply to the various operation types for this account; all KYC checks should first check if active new rules for a given account exist in this table (and apply specified measures); if not, it should check the default rules to decide if a measure is required';
CREATE INDEX legitimization_outcomes_active
ON legitimization_outcomes(h_payto)
WHERE is_active;
- CREATE TABLE kyc_setups
- (kyc_setup_serial_id BIGSERIAL UNIQUE
+ CREATE TABLE legitimization_processes
+ (legitimization_process_serial_id BIGSERIAL UNIQUE
,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64)
- REFERENCES wire_targets (h_payto)
+ REFERENCES wire_targets (wire_target_h_payto)
,start_time INT8 NOT NULL
,expiration_time INT8 NOT NULL DEFAULT (0)
- ,legitimization_measure_serial_id BIGINT
+ ,legitimization_measure_serial_id INT8
REFERENCES legitimization_measures (legitimization_measure_serial_id)
- ,measure_index INT8
- ,provider_section VARCHAR NOT NULL
- ,provider_user_id VARCHAR DEFAULT NULL
- ,provider_legitimization_id VARCHAR DEFAULT NULL
+ ,measure_index INT4
+ ,provider_section TEXT NOT NULL
+ ,provider_user_id TEXT DEFAULT NULL
+ ,provider_legitimization_id TEXT DEFAULT NULL
,redirect_url TEXT DEFAULT NULL
,finished BOOLEAN DEFAULT (FALSE)
)
PARTITION BY HASH (h_payto);
- COMMENT ON TABLE kyc_setups
+ COMMENT ON TABLE legitimization_processes
IS 'here we track KYC processes we initiated with external providers; the main reason is so that we do not initiate a second process when an equivalent one is still active; note that h_payto, provider_section, jcontext must match and the process must not be finished or expired for an existing redirect_url to be re-used; given that clients may voluntarily initiate KYC processes, there may not always be a legitimization_measure that triggered the setup';
- COMMENT ON COLUMN kyc_setups.h_payto
+ COMMENT ON COLUMN legitimization_processes.h_payto
IS 'foreign key linking the entry to the wire_targets table, NOT a primary key (multiple KYC setups are possible per wire target)';
- COMMENT ON COLUMN kyc_setups.start_time
+ COMMENT ON COLUMN legitimization_processes.start_time
IS 'when was the legitimization process initiated';
- COMMENT ON COLUMN kyc_setups.expiration_time
+ COMMENT ON COLUMN legitimization_processes.expiration_time
IS 'when does the process expire (and needs to be manually set up again)';
- COMMENT ON COLUMN kyc_setups.measure_index
+ COMMENT ON COLUMN legitimization_processes.measure_index
IS 'index of the measure in legitimization_measures that was selected for this KYC setup; NULL if legitimization_measure_serial_id is NULL; enables determination of the context data provided to the external process';
- COMMENT ON COLUMN kyc_setups.provider_section
+ COMMENT ON COLUMN legitimization_processes.provider_section
IS 'Configuration file section with details about this provider';
- COMMENT ON COLUMN kyc_setups.provider_user_id
+ COMMENT ON COLUMN legitimization_processes.provider_user_id
IS 'Identifier for the user at the provider that was used for the legitimization. NULL if provider is unaware.';
- COMMENT ON COLUMN kyc_setups.provider_legitimization_id
+ COMMENT ON COLUMN legitimization_processes.provider_legitimization_id
IS 'Identifier for the specific legitimization process at the provider. NULL if legitimization was not started.';
- COMMENT ON COLUMN kyc_setups.legitimization_measure_serial_id
+ COMMENT ON COLUMN legitimization_processes.legitimization_measure_serial_id
IS 'measure that enabled this setup, NULL if client voluntarily initiated the process';
- COMMENT ON COLUMN kyc_setups.redirect_url
+ COMMENT ON COLUMN legitimization_processes.redirect_url
IS 'Where the user should be redirected for this external KYC process';
- COMMENT ON COLUMN kyc_setups.finished
+ COMMENT ON COLUMN legitimization_processes.finished
IS 'set to TRUE when the specific legitimization process is finished';
CREATE TABLE kyc_attributes
- (kyc_attributes_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+ (kyc_attributes_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY
,h_payto BYTEA PRIMARY KEY CHECK (LENGTH(h_payto)=32)
- REFERENCES wire_targets (h_payto)
- ,kyc_prox BYTEA NOT NULL CHECK (LENGTH(kyc_prox)=32)
- ,kyc_setup_serial_id INT8 NOT NULL
- REFERENCES kyc_setups (kyc_setup_serial_id)
+ REFERENCES wire_targets (wire_target_h_payto)
+ ,legitimization_process_serial_id INT8
+ REFERENCES legitimization_processes (legitimization_process_serial_id)
+ DEFAULT NULL
,collection_time INT8 NOT NULL
,expiration_time INT8 NOT NULL
,trigger_outcome_serial INT8 NOT NULL
@@ -1472,10 +1367,8 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
COMMENT ON COLUMN kyc_attributes.h_payto
IS 'identifies the account this is about';
- COMMENT ON COLUMN kyc_attributes.kyc_prox
- IS 'for proximity search on encrypted data';
- COMMENT ON COLUMN kyc_attributes.kyc_setup_serial_id
- IS 'serial ID of the KYC setup that resulted in these attributes';
+ COMMENT ON COLUMN kyc_attributes.legitimization_process_serial_id
+ IS 'serial ID of the legitimization process that resulted in these attributes, NULL if the attributes are from a form directly supplied by the account owner via a form';
COMMENT ON COLUMN kyc_attributes.collection_time
IS 'when were these attributes collected';
COMMENT ON COLUMN kyc_attributes.expiration_time
@@ -1486,10 +1379,10 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
IS 'encrypted JSON object with the attribute data the check provided';
CREATE TABLE aml_history
- (aml_history_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+ (aml_history_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY
,h_payto BYTEA CHECK (LENGTH(h_payto)=32)
- REFERENCES wire_targets (h_payto)
- ,legitimization_outcome INT8 NOT NULL
+ REFERENCES wire_targets (wire_target_h_payto)
+ ,outcome_serial_id INT8 NOT NULL
REFERENCES legitimization_outcomes (outcome_serial_id)
,justification TEXT NOT NULL
,decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)
@@ -1497,13 +1390,13 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
COMMENT ON TABLE aml_history
IS 'Records decisions by AML staff with the respective signature and free-form justification.';
- COMMENT ON COLUMN aml_history.legitimization_outcome
+ COMMENT ON COLUMN aml_history.outcome_serial_id
IS 'Actual outcome for the account (included in what decider_sig signs over)';
COMMENT ON COLUMN aml_history.decider_sig
IS 'Signature key of the staff member affirming the AML decision; of type AML_DECISION';
CREATE TABLE kyc_events
- (event_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+ (kyc_event_serial_id INT8 GENERATED BY DEFAULT AS IDENTITY
,event_timestamp INT8 NOT NULL
,event_type TEXT NOT NULL);
@@ -1517,74 +1410,13 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
The ``jmeasures`` JSON in the ``legitimization_measures``
-table has is of type `LegitimizationMeasures`:
-
-.. ts:def:: LegitimizationMeasures
-
- interface LegitimizationMeasures {
-
- // Array of legitimization measures that
- // are to be applied.
- measures: MeasureInformation[];
-
- // True if the client is expected to eventually satisfy all requirements.
- // Default (if missing) is false.
- is_and_combinator?: boolean;
- }
-
+table is of type `LegitimizationMeasures`.
The ``jnew_rules`` JSON in the ``legitimization_outcomes``
-table has is of type `LegitimizationRules`:
-
-.. ts:def:: LegitimizationRules
-
- interface LegitimizationRules {
-
- // Measure to apply when the expiration time is
- // reached. If not set, we refer to the default
- // set of rules (and the default account state).
- successor_measure?: string;
-
- // Legitimization rules that are to be applied
- // to this account.
- rules: KycRule[];
-
- // Custom measures that KYC rules may refer to.
- custom_measures: { "name" : MeasureInformation };
- }
-
-
-The ``jproperties`` JSON in the ``legitimization_outcomes`` table has is of
-type `AccountProperties`. All fields in this object are optional. The actual
-properties collected depend fully on the discretion of the exchange operator;
-however, some common fields are standardized and thus described here.
-
-.. ts:def:: AccountProperties
-
- interface AccountProperties {
-
- // True if this is a politically exposed account.
- // Rules for classifying accounts as politically
- // exposed are country-dependent.
- pep?: boolean;
-
- // True if this is a sanctioned account.
- // Rules for classifying accounts as sanctioned
- // are country-dependent.
- sanctioned?: boolean;
-
- // True if this is a high-risk account.
- // Rules for classifying accounts as at-risk
- // are exchange operator-dependent.
- high_risk?: boolean;
-
- // Business domain of the account owner.
- // The list of possible business domains is
- // operator- or country-dependent.
- business_domain?: string;
-
- }
+table is of type `LegitimizationRuleSet`.
+The ``jproperties`` JSON in the ``legitimization_outcomes`` table is of
+type `AccountProperties`.
KYC forms
@@ -1596,25 +1428,24 @@ exteral KYC check and redirecting there. FORM is about displaying a particular
(HTML) form to the user and POSTing the entered information directly with the
exchange. Here we describe the forms that must be supported:
-* CHOICE: Asks the client a multiple-choice question.
- The context must include "choices: string[]" with
- a list of choices to show. Used, for example, to
- ask a client if they are an individual or a business.
- The resulting HTML FORM field name must be
- "choice" and it must be mapped to strings from the
- choices list.
-* UPLOAD: Asks the client to upload a single file.
- The context may include "extensions: string[]" with
- a list of allowed file extensions the client's file
- must end with (e.g. "png", "pdf", "gif"). In the
- absence of this context, any file may be uploaded.
- The context may also include "size_limit: Integer" with
- the maximum file size in bytes that can be uploaded.
- The resulting HTML FORM must have two fields,
- "filename" and "filedata". "filename" must be
- set to the basename of the original file (to the
- extend that it is available), and "filedata"
- to the base64-encoding of the uploaded data.
+* **CHOICE**: Asks the client a multiple-choice question. The context must
+ include "choices: string[]" with a list of choices to show. Used, for
+ example, to ask a client if they are an individual or a business. The
+ resulting HTML FORM field name must be "choice" and it must be mapped to
+ strings from the choices list.
+
+* **UPLOAD**: Asks the client to upload a single file.
+ The context must include a ``validity_duration`` which
+ will be converted to the ``expiration_time`` for
+ the uploaded data. The context may furthermore include
+ ``extensions?: string[]`` with a list of allowed file extensions the client's
+ file must end with (e.g. "png", "pdf", "gif"). In the absence of this
+ context, any file may be uploaded. The context may also include a
+ ``size_limit?: Integer`` with the maximum file size in bytes that can be
+ uploaded. The resulting HTTP POST should provide at least two fields, "filename" and
+ "filedata". "filename" must be set to the basename of the original file (to
+ the extend that it is available), and "filedata" to the base64-encoding of
+ the uploaded data.
As with other SPA checks, the KYC form should also show
the description of the check.
@@ -1824,12 +1655,12 @@ For ``/kyc-webhook/``:
Types of KYC events
^^^^^^^^^^^^^^^^^^^
-The ``/aml/kyc-statistics`` endpoint exposes statistics
-for various KYC event types.
+The ``/aml/$OFFICER_PUB/kyc-statistics`` endpoint exposes statistics for
+various KYC event types.
-We will initially support the use of the following types
-of KYC events in the SPA (and have a dialog to show the
-total number of any of these for any specified time range):
+We will initially support the use of the following types of KYC events in the
+SPA (and have a dialog to show the total number of any of these for any
+specified time range):
* account-open
* account-closed
diff --git a/design-documents/046-mumimo-contracts.rst b/design-documents/046-mumimo-contracts.rst
index 8cb35316..7d2197d4 100644
--- a/design-documents/046-mumimo-contracts.rst
+++ b/design-documents/046-mumimo-contracts.rst
@@ -326,7 +326,7 @@ The contract terms v1 will have the following structure:
// Start of the validity period of the token.
valid_after: Timestamp;
- // Number of tokens to be yelded.
+ // Number of tokens to be issued.
// Defaults to one if the field is not provided.
number?: Integer;
diff --git a/design-documents/053-wallet-ui.rst b/design-documents/053-wallet-ui.rst
index 7b5fdf73..792f58e2 100644
--- a/design-documents/053-wallet-ui.rst
+++ b/design-documents/053-wallet-ui.rst
@@ -132,11 +132,6 @@ Issues
the "add" and "send" buttons on this page.
* WebEx (image): Screenshot does not show any
pending transactions.
-* Android (text): Uses "Exchange:" which is
- not user-facing terminology. Should only show the URLs.
-* Android (minor): Screenshot shows only a "pending"
- badge, which seems redundant given "+10 KUDOS inbound"
- (too much information?)
* iOS (major): Way too much detail shown, way too
much explanation text, too many operations
(send money, request payment, spend test money!!!!);
@@ -150,7 +145,6 @@ Issues
'tab' entirely!
* iOS (text): bad icon for "pending outgoing", the
"minus" sign does not give me good associations here
-* Android (text): Title should be *Balances* (plural)
* Android (minor): No "add" and "Send $CURRENCY" buttons
on this page.
@@ -282,22 +276,12 @@ Issues
* WebEx (text): Iconography (T), (W) is not as
nice as iconography in on Android.
-* WebEx (text): The balance is not labeled as the balance.
-* WebEx (text): Button is labeled as "Add",
- while above says to use "Receive" which is
- a better dual with "Send".
* WebEx (image): Screenshot does not show any
pending transactions.
-* WebEx (nit): Android has different order of
- items on top (send+receive, then balance).
- This places the balance on top of the other
- amounts, which is nicer.
* Android (text): Iconography (same icon for push
payments and debit and POS) and again same icon
for invoice and withdraw) is sub-optimal. Should
pick unique icons for each type of operation.
-* Android (text): Button labels should just be
- "Send" and "Receive" (without "funds").
* iOS (text): Iconography (+ / -) is also not
expressive and redundant with the colors.
* iOS (text): Sign of the operation (+ / -) should
@@ -324,13 +308,8 @@ Issues
exchange URL for withdraw
* iOS (major): The balance is not shown. The
"send" and "receive" buttons are missing.
-* WebEx (minor): lacks search button (see Android)
* iOS (minor): has top/buttom scroll buttons not present
in other UIs (likely too much, better to have search!)
-* WebEx (minor): unclear how to do bulk-deletion;
- other platforms make it easy to select multiple
- rows. Maybe have some checkboxes and then a
- "delete selected" button at the bottom?
* iOS (critical): the buttons to send/receive funds
are not even shown in the screenshot, likely because
of balances vs. banking distinction, which should
@@ -596,18 +575,11 @@ Actions
Issues
^^^^^^
-* WebEx(text): wire transfer subject is last, should be first!
-* WebEx(text): Receiver name is not shown (except within payto URI); must be shown above IBAN!
- and only a link shown (see iOS)
-* WebEx(text): PaytoURI should not be expanded on-screen
- and only a link shown (see iOS)
* All(text): if there is no fee, no point in showing
the amount **3** times. Maybe only show the amount
on top with the wire transfer details, and then at
the bottom show the fee ONCE *if* there is one?
-* Android(text): uses "exchange", which is verboten
* iOS(text): receiver name is missing, MUST be before IBAN
-* Android(text): wire transfer subject is third, should be first
* WebEx(screenshot): the screenshot does not show how to select an alternative bank (see: Netzbon). Would be nice to show that.
* Android(screenshot): the screenshot does not show how to select an alternative bank (see: Netzbon). Would be nice to show that.
@@ -666,8 +638,6 @@ Issues
* WebEx(text): not point in showing details if there are no fees.
* iOS(text): not point in showing details if there are no fees.
-* Android(text): still talks about 'exchange'
-* Android(text): has the amount twice, useless without fees
* iOS(text): status: Done is unnecessary, if we show this screen, it will always be 'done' (ok, theoretically in the middle of withdrawing, but the wire transfer will be done; but then maybe show 'incoming' but hide the status once really "done").
* unclear: "Delete" vs. 'Delete from history" --- two
styles, two translations, gain?
@@ -729,9 +699,6 @@ Actions
Issues
^^^^^^
-* WebEx(text) has additional actions "clear" (not necessary, I can manually clear), "Read QR from file" (who does that!???!) that should be removed.
-* Android(text) has button label "OK", should probably be "Open" for uniformity.
-* Android(text) has "Enter Taler URI", while WebEx has clearer text "enter taler:// URI".
* iOS (screenshot): lacks dialog (or screenshot?) entirely, not sure if we need manual URL-entry on mobile though, so could be OK!
@@ -782,9 +749,7 @@ Actions
Issues
^^^^^^
-* WebEx(Text): showing price/total without fees is a bit excessive, could be simplified in absence of fees
* WebEx(Text): "Valid until" is likely confusing, not shown on other platforms. Maybe only show "offer expired" instead of pay button (on all platforms!)?
-* WebEx(Text): Receipt number is not shown on other platforms at this time, which also makes sense as it is only an order number and certainly nothing like a receipt before payment! (Remove!)
* WebEx(Screenshot): would be good to show the dialog with expanded order details as well, maybe even expand by default like on Android (and forgo the button?)
* WebEx(Screenshot): would be good to show a version of the dialog with fees in the screenshot.
* iOS(text): the label 'Summary' appears only on iOS, and seems unnecessary. Especially since no long-form is available anywhere.
@@ -846,8 +811,6 @@ Actions
Issues
^^^^^^
-* WebEx(text): Details should be simplified if there are no fees
-* Android(text): details say receipt, but WebEx uses the slightly more accurate "Invoice ID". Could also use "Order #"?
* iOS(major): delete button is missing, instead only has "Done"
@@ -969,8 +932,6 @@ Actions
Issues
^^^^^^
-* WebEx(text): 20 days is odd, Android has 30 days instead
-* Android(text): Webex uses "1 Week" instead of "7 days", let's use "week".
* iOS(text): 3 min/ 1h are inconsistent; other wallets have 1 day, 7 days, 30 days. We should be consistent.
diff --git a/design-documents/054-dynamic-form.rst b/design-documents/054-dynamic-form.rst
index d93b3684..5567fc60 100644
--- a/design-documents/054-dynamic-form.rst
+++ b/design-documents/054-dynamic-form.rst
@@ -1,10 +1,13 @@
-DD 54: Dynamic Web Form
-#######################
+DD 54: Dynamic Form
+###################
Summary
=======
-This document outlines the approach for implementing a dynamic web form feature.
+This document outlines the design of forms defined in the
+backend in a JSON file which will be rendered by a client
+for asking information to a person.
+
Motivation
==========
@@ -12,12 +15,16 @@ Motivation
Currently, creating a new form for a web app involves coding a new
page with HTML, CSS, and JS. Exchange AML requires multiple forms,
and different instances may have distinct forms based on jurisdiction.
+Being able to define forms in a JSON file that a client software
+(not just web SPA) could use to ask the information helps to change
+it without requiring a new upgrade of the client app.
Requirements
============
-A form consist of a layout and a set of fields.
+A form consist of a layout, a set of fields and metadata required to
+recognice which form configuration was used to produce the saved value.
Layout requirements
-------------------
@@ -41,159 +48,609 @@ Fields requirements
complex composite structure.
+Metadata requirements
+---------------------
+
+* **identification**: the form configuration instance should have an unique
+ non reusable id.
+
+* **label**: the form should have a name recognizable for the user
+
+* **version**: the same form, with the same id, could be updated. This will
+ increase the version number. A newer form should support older forms.
+
Proposed Solutions
==================
-Forms are initialized using a flexible structure defined by the
-TypeScript interface FormType<T>. This interface comprises properties
-such as value (current form data), initial (initial form data for resetting),
-readOnly (flag to disable input), onUpdate (callback on form data update),
-and computeFormState (function to derive the form state based on current data).
+The propose solution defines the structure of a form, the fields and additional
+type form-configuration which links a form with a set of fields.
+
+Form metadata
+-------------
+
+This is the root object of the configuration.
+.. code-block:: typescript
+
+ type FormMetadata = {
+ label: string;
+ id: string;
+ version: number;
+ config: FormConfiguration;
+ };
+
+
+Form configuration
+------------------
+
+Defies a basic structure and the set of fields the form is going to have.
+
+The ``FormConfiguration`` is an enumerated type which list can be extended in the
+future.
.. code-block:: typescript
- interface FormType<T extends object> {
- value: Partial<T>;
- initial?: Partial<T>;
- readOnly?: boolean;
- onUpdate?: (v: Partial<T>) => void;
- computeFormState?: (v: Partial<T>) => FormState<T>;
+ type FormConfiguration = DoubleColumnForm;
+
+ type DoubleColumnForm = {
+ type: "double-column";
+ design: DoubleColumnFormSection[];
}
+ type DoubleColumnFormSection = {
+ title: string;
+ description?: string;
+ fields: UIFormElementConfig[];
+ };
-``T``: is the type of the result object
-``value``: is a reference to the current value of the result
-``initial``: data for resetting
-``readOnly``: when true, fields won't allow input
-``onUpdate``: notification of the result update
-``computeFormState``: compute a new state of the form based on the current value
-Form state have the same shape of ``T`` but every field type is ``FieldUIOptions``.
+Form fields
+-----------
-Fields type can be:
- * strings
- * numbers
- * boolean
- * arrays
- * object
+A form can have two type of element: decorative/informative or input.
-The field type ``AmountJson`` and ``AbsoluteTime`` are opaque since field is used as a whole.
+An example of a decorative element is a grouping element which make all the fields
+inside the group look into the same section. This element will not allow the user
+to enter information and won't produce any value in the resulting JSON.
-The form can be instanciated using
+An example of an input field is a text field which allows the user to enter text.
+This element should have an ``id`` which will point into the location in which the
+value will be stored in the resulting JSON. Note that two field in the same form
+with the same ``id`` will result in undefined behavior.
+
+The ``UIFormElementConfig`` is an enumerated type with all type of fields supported
+
+.. code-block:: typescript
+
+ type UIFormElementConfig =
+ | UIFormElementGroup
+ | UIFormElementCaption
+ | UIFormFieldAbsoluteTime
+ | UIFormFieldAmount
+ | UIFormFieldArray
+ | UIFormFieldChoiseHorizontal
+ | UIFormFieldChoiseStacked
+ | UIFormFieldFile
+ | UIFormFieldInteger
+ | UIFormFieldSelectMultiple
+ | UIFormFieldSelectOne
+ | UIFormFieldText
+ | UIFormFieldTextArea
+ | UIFormFieldToggle;
+
+
+All form elements should extend from ``UIFieldElementDescription`` which defines a base
+configuration to show a field.
.. code-block:: typescript
- import { FormProvider } from "@gnu-taler/web-util/browser";
+ type UIFieldElementDescription = {
+ /* label if the field, visible for the user */
+ label: string;
+
+ /* long text to be shown on user demand */
+ tooltip?: string;
+
+ /* short text to be shown close to the field, usually below and dimmer*/
+ help?: string;
+
+ /* name of the field, useful for a11y */
+ name: string;
+ /* if the field should be initially hidden */
+ hidden?: boolean;
+
+ };
+
+That will be enough for a decorative form element (like group element or
+a text element) but if it defines an input field then it should extend
+from ``UIFormFieldBaseConfig`` which add more information to the previously
+defined ``UIFieldElementDescription``.
-Then the field component can access all the properties by the ``useField(name)`` hook,
-which will return
.. code-block:: typescript
- interface InputFieldHandler<Type> {
- value: Type;
- onChange: (s: Type) => void;
- state: FieldUIOptions;
- isDirty: boolean;
- }
+ type UIFormFieldBaseConfig = UIFieldElementDescription & {
+ /* example to be shown inside the field */
+ placeholder?: string;
+ /* show a mark as required */
+ required?: boolean;
-``value``: the current value of the field
-``onChange``: a function to call anytime the user want to change the value
-``state``: the state of the field (hidden, error, etc..)
-``isDirty``: if the user already tried to change the value
+ /* readonly and dim */
+ disabled?: boolean;
-A set of common form field exist in ``@gnu-taler/web-util``:
+ /* conversion id to convert the string into the value type
+ the id should be known to the ui impl
+ */
+ converterId?: string;
- * InputAbsoluteTime
- * InputAmount
- * InputArray
- * InputFile
- * InputText
- * InputToggle
+ /* property id of the form */
+ id: UIHandlerId;
+ };
-and should be used inside a ``Form`` context.
+ /**
+ * string which defined a json path
+ *
+ */
+ type UIHandlerId = string
-.. code-block:: none
- function MyFormComponent():VNode {
- return <FormProvider>
- <InputAmount name="amount" />
- <InputText name="subject" />
- <button type="submit"> Confirm </button>
- </FormProvider>
- }
+The ``id`` property defines the location in which this information is going
+to be saved in the JSON result. Formally formally, it should be a ``dot-selector``
+
+.. code-block:: ini
+
+ dot-selector = "." dot-member-name
+ dot-member-name = name-first *name-char
+ name-first = ALPHA / "_"
+ name-char = DIGIT / name-first
+
+ DIGIT = %x30-39 ; 0-9
+ ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+All the input fields will create a string value located where the id
+points, unless a ``convertedId`` is specified. The ``convertedId`` is a reference
+to a converter that the client software implements. For example, an input field
+with ``convertedId = "Taler.Amount"`` will transform the value the user
+entered into a *AmountString* with the currency in the configuration.
+
+
+Description of supported fields
+-------------------------------
+
+All of this fields defines an UI handler which help the user to input
+the value with as handy as possible. The type of the field doesn't define
+the type of the value in the resulting JSON, that's defined by the ``converterId``.
+
+Decorative elements
+```````````````````
+
+To show some additional text
+
+.. code-block:: typescript
+
+ type UIFormElementCaption = { type: "caption" } & UIFieldElementDescription;
+
+To group fields in the UI and maybe show a collapsable handler.
+
+.. code-block:: typescript
+
+ type UIFormElementGroup = {
+ type: "group";
+ fields: UIFormElementConfig[];
+ } & UIFieldElementDescription;
+
Example
---------
+'''''''
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Decorative elements",
+ "fields": [
+ {
+ "type": "caption",
+ "name": "cap",
+ "label": "This is a caption"
+ },
+ {
+ "type": "group",
+ "name": "group",
+ "label": "The first name and last name are in a group",
+ "fields": [
+ {
+ "type": "text",
+ "name": "firstName",
+ "id": ".person.name",
+ "label": "First name"
+ },
+ {
+ "type": "text",
+ "name": "lastName",
+ "id": ".person.lastName",
+ "label": "Last name"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.decorative-elements.png
+ :width: 400
+
+Time input
+``````````
+
+This may be rendered as a calendar
+
+.. code-block:: typescript
+
+ type UIFormFieldAbsoluteTime = {
+ type: "absoluteTimeText";
+ max?: TalerProtocolTimestamp;
+ min?: TalerProtocolTimestamp;
+ pattern: string;
+ } & UIFormFieldBaseConfig;
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Time inputs",
+ "fields": [
+ {
+ "type": "absoluteTime",
+ "name": "thedate",
+ "id": ".birthdate",
+ "converterId": "Taler.AbsoluteTime",
+ "help": "the day you born",
+ "pattern":"dd/MM/yyyy",
+ "label": "Birthdate"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.time.png
+ :width: 400
+
+
+Amount input
+````````````
+
+Money input.
+
+.. code-block:: typescript
+
+ type UIFormFieldAmount = {
+ type: "amount";
+ max?: Integer;
+ min?: Integer;
+ currency: string;
+ } & UIFormFieldBaseConfig;
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Amount inputs",
+ "fields": [
+ {
+ "type": "amount",
+ "name": "thedate",
+ "id": ".amount",
+ "converterId": "Taler.Amount",
+ "help": "how much do you have?",
+ "currency":"EUR",
+ "label": "Amount"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.amount.png
+ :width: 400
-Consider a form shape represented by the TypeScript type:
+
+List input
+``````````
+
+This input allows to enter more than element in the same field, and the
+resulting JSON will have a json list. The UI should show the elements
+already present in the list, and for that it will use ``labelFieldId``.
.. code-block:: typescript
- type TheFormType = {
- name: string,
- age: number,
- savings: AmountJson,
- nextBirthday: AbsoluteTime,
- pets: string[],
- addres: {
- street: string,
- city: string,
+ type UIFormFieldArray = {
+ type: "array";
+ // id of the field shown when the array is collapsed
+ labelFieldId: UIHandlerId;
+ fields: UIFormElementConfig[];
+ } & UIFormFieldBaseConfig;
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Amount inputs",
+ "fields": [
+ {
+ "type": "array",
+ "name": "people",
+ "id": ".people",
+ "help": "who is coming to the party?",
+ "labelFieldId": ".name",
+ "fields": [{
+ "type": "text",
+ "name": "firstName",
+ "id": ".name",
+ "label": "First name"
+ },
+ {
+ "type": "text",
+ "name": "lastName",
+ "id": ".lastName",
+ "label": "Last name"
+ }],
+ }
+ ]
+ }
+ ]
+ }
}
+
+
+.. image:: ../screenshots/dynamic-forms.list.png
+ :width: 400
+
+Choice input
+````````````
+
+To be used when the user need to choose on predefined values
+
+.. code-block:: typescript
+
+ interface SelectUiChoice {
+ label: string;
+ description?: string;
+ value: string;
}
-An example instance of this form could be:
+A set of buttons next to each other
.. code-block:: typescript
- const theFormValue: TheFormType = {
- name: "Sebastian",
- age: 15,
- pets: ["dog","cat"],
- address: {
- street: "long",
- city: "big",
+ type UIFormFieldChoiseHorizontal = {
+ type: "choiceHorizontal";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+
+A set of buttons next on top of each other
+
+.. code-block:: typescript
+
+ type UIFormFieldChoiseStacked = {
+ type: "choiceStacked";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+A drop down list to select one of the elements
+
+.. code-block:: typescript
+
+ type UIFormFieldSelectOne = {
+ type: "selectOne";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+A drop down list to select multiple of the element, which
+will produce a list of values in the resulting JSON.
+
+.. code-block:: typescript
+
+ type UIFormFieldSelectMultiple = {
+ type: "selectMultiple";
+ max?: Integer;
+ min?: Integer;
+ unique?: boolean;
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Choice inputs",
+ "fields": [
+ {
+ "type": "choiceHorizontal",
+ "name": "food",
+ "label": "Food",
+ "id": ".food",
+ "choices": [
+ {
+ "value": "meat",
+ "label": "Meat"
+ },
+ {
+ "value": "sushi",
+ "label": "Sushi"
+ },
+ {
+ "value": "taco",
+ "label": "Taco"
+ },
+ {
+ "value": "salad",
+ "label": "Salad"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.choice.png
+ :width: 400
+
+File input
+``````````
+
+.. code-block:: typescript
+
+ type UIFormFieldFile = {
+ type: "file";
+ maxBytes?: Integer;
+ minBytes?: Integer;
+ // comma-separated list of one or more file types
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept#unique_file_type_specifiers
+ accept?: string;
+ } & UIFormFieldBaseConfig;
+
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "File inputs",
+ "fields": [
+ {
+ "type": "file",
+ "name": "photo",
+ "id": ".photo",
+ "label": "Photo",
+ "accept": "*.png"
+ }
+ ]
+ }
+ ]
}
}
+.. image:: ../screenshots/dynamic-forms.file.png
+ :width: 400
+
+Number input
+````````````
+
+.. code-block:: typescript
+
+ type UIFormFieldInteger = {
+ type: "integer";
+ max?: Integer;
+ min?: Integer;
+ } & UIFormFieldBaseConfig;
+
+
+.. image:: ../screenshots/dynamic-forms.number.png
+ :width: 400
+
+Text input
+``````````
+
+A simple line of text
-For such a form, a valid state can be computed using a function like
-``computeFormStateBasedOnFormValues``, returning an object indicating
-the state of each field, including properties such as ``hidden``,
-``disabled``, and ``required``.
-
-
-.. code-block:: javascript
-
- function computeFormStateBasedOnFormValues(formValues): {
- //returning fixed state as an example
- //the return state will be commonly be computed from the values of the form
- return {
- age: {
- hidden: true,
- },
- pets: {
- disabled: true,
- elements: [{
- disabled: false,
- }],
- },
- address: {
- street: {
- required: true,
- error: "the street name was not found",
- },
- city: {
- required: true,
- },
- },
+.. code-block:: typescript
+
+ type UIFormFieldText = { type: "text" } & UIFormFieldBaseConfig;
+
+A bigger multi-line of text
+
+.. code-block:: typescript
+
+ type UIFormFieldTextArea = { type: "textArea" } & UIFormFieldBaseConfig;
+
+
+.. image:: ../screenshots/dynamic-forms.text.png
+ :width: 400
+
+Boolean input
+`````````````
+
+.. code-block:: typescript
+
+ type UIFormFieldToggle = { type: "toggle" } & UIFormFieldBaseConfig;
+
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Boolean inputs",
+ "fields": [
+ {
+ "type": "toggle",
+ "name": "the_question",
+ "id": ".the_question",
+ "label": "Yes or no?"
+ }
+ ]
+ }
+ ]
}
}
+.. image:: ../screenshots/dynamic-forms.boolean.png
+ :width: 400
+
+Examples
+========
diff --git a/frags/installing-ubuntu.rst b/frags/installing-ubuntu.rst
index f31b4ec6..89ca4dc9 100644
--- a/frags/installing-ubuntu.rst
+++ b/frags/installing-ubuntu.rst
@@ -17,7 +17,11 @@ For Ubuntu Mantic use this instead:
deb [signed-by=/etc/apt/keyrings/taler-systems.gpg] https://deb.taler.net/apt/ubuntu/ mantic taler-mantic
-The last line is crucial, as it adds the GNU Taler packages.
+For Ubuntu Noble use this instead:
+
+.. code-block::
+
+ deb [signed-by=/etc/apt/keyrings/taler-systems.gpg] https://deb.taler.net/apt/ubuntu/ noble taler-noble
Next, you must import the Taler Systems SA public package signing key
into your keyring and update the package lists:
diff --git a/images/regional-arch.png b/images/regional-arch.png
deleted file mode 100644
index a3691aea..00000000
--- a/images/regional-arch.png
+++ /dev/null
Binary files differ
diff --git a/index.rst b/index.rst
index bd3b30d6..417e3a1b 100644
--- a/index.rst
+++ b/index.rst
@@ -18,6 +18,7 @@
@author Sree Harsha Totakura
@author Marcello Stanisci
@author Christian Grothoff
+ @author Javier Sepulveda
GNU Taler Documentation
=======================
@@ -63,6 +64,7 @@ Documentation Overview
taler-auditor-manual
taler-developer-manual
libeufin/index
+ system-administration/index
design-documents/index
global-licensing
manindex
diff --git a/libeufin/index.rst b/libeufin/index.rst
index c77255b0..4fd7668c 100644
--- a/libeufin/index.rst
+++ b/libeufin/index.rst
@@ -31,4 +31,5 @@ LibEuFin is a project providing free software tooling for European FinTech.
bank-manual
regional-automated-manual
regional-custom-manual
+ regional-manual-use
setup-ebics-at-postfinance
diff --git a/libeufin/regional-automated-manual.rst b/libeufin/regional-automated-manual.rst
index f416d1b9..39dfd3f6 100644
--- a/libeufin/regional-automated-manual.rst
+++ b/libeufin/regional-automated-manual.rst
@@ -241,7 +241,6 @@ manual setup and in the the manpage of ``taler-exchange-offline``.
.. include:: ../frags/regional-system-on.rst
.. include:: ../frags/deploying-tos.rst
-.. include:: ../frags/regional-manual-use.rst
Installing Updates
diff --git a/libeufin/regional-custom-manual.rst b/libeufin/regional-custom-manual.rst
index 7da39c96..4a347791 100644
--- a/libeufin/regional-custom-manual.rst
+++ b/libeufin/regional-custom-manual.rst
@@ -141,7 +141,6 @@ account with "CHF".
.. include:: ../frags/regional-system-on.rst
.. include:: ../frags/deploying-tos.rst
-.. include:: ../frags/regional-manual-use.rst
Maintenance
diff --git a/frags/regional-manual-use.rst b/libeufin/regional-manual-use.rst
index 7566d622..ff9f38f8 100644
--- a/frags/regional-manual-use.rst
+++ b/libeufin/regional-manual-use.rst
@@ -1,3 +1,22 @@
+..
+ 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 Florian Dold
+ @author Marcello Stanisci
+ @author Christian Grothoff
+
.. _regional-use:
Using the Regional Currency
diff --git a/manpages/libeufin-nexus.1.rst b/manpages/libeufin-nexus.1.rst
index a0ed99ff..5eb42cab 100644
--- a/manpages/libeufin-nexus.1.rst
+++ b/manpages/libeufin-nexus.1.rst
@@ -93,7 +93,7 @@ Its options are as follows:
Configure logging to use LOGLEVEL.
Uploaded documents will be stored *before* being submitted to the bank. This directory would contain several directories, each named after the ``YYYY-MM-DD/submit`` format. The pain.001 file would then be named in the following schema: ``$microseconds_pain.001.xml``.
**--transient**
- This flag, enabled by default, causes the command to check the database and submit only once, and then return.
+ This flag causes the command to check the database and submit only once, and then return.
ebics-fetch
@@ -105,6 +105,8 @@ The files type can be given as an argument to select what will be fetched. If no
* ``acknowledgement``: EBICS acknowledgement, retrieves the status of EBICS orders.
* ``status``: Payment status, retrieves status of pending debits.
+* ``report``: Account intraday reports, retrieves the history of confirmed debits and credits.
+* ``statement``: Account statements, retrieves the history of confirmed debits and credits.
* ``notification``: Debit & credit notifications, retrieves the history of confirmed debits and credits.
**-h** \| **--help**
@@ -117,10 +119,26 @@ The files type can be given as an argument to select what will be fetched. If no
Log EBICS content at SAVEDIR.
Downloaded documents will be stored *before* being ingested in the database. This directory would contain several directories, each named after the ``YYYY-MM-DD/fetch`` format. The stored files would then be named after the following schema: ``$microseconds_$filename``. Exception to this naming scheme are the HAC responses, since they do not get any filename assigned by the ZIP archive (they are sent unzipped). Their naming scheme is: ``$microseconds_HAC_response.pain.002.xml``.
**--transient**
- This flag, enabled by default, causes the command to perform one download and return.
+ This flag causes the command to perform one download and return.
**--pinned-start**
Only supported in --transient mode, this option lets specify the earliest timestamp of the downloaded documents. The latest timestamp is always the current time.
+serve
+-----
+
+This command starts the HTTP server.
+
+Its options are as follows:
+
+**-h** \| **--help**
+ Print short help on options.
+**-c** \| **--config** *FILENAME*
+ Specifies the configuration file.
+**-L** \| **--log** *LOGLEVEL*
+ Configure logging to use LOGLEVEL.
+**--check**
+ This flag causes the command to check whether an API is in use (if it's useful to start the HTTP server) and to output 0 if at least one API is enabled, otherwise 1.
+
initiate-payment
----------------
diff --git a/manpages/libeufin-nexus.conf.5.rst b/manpages/libeufin-nexus.conf.5.rst
index 9100f8fe..8aabe883 100644
--- a/manpages/libeufin-nexus.conf.5.rst
+++ b/manpages/libeufin-nexus.conf.5.rst
@@ -116,8 +116,8 @@ HOST_BASE_URL
BANK_DIALECT
Name of the following combination: EBICS version and ISO20022 recommendations
- that Nexus would honor in the communication with the bank. Currently only the
- 'postfinance' value is supported.
+ that Nexus would honor in the communication with the bank. Currently only the
+ ``postfinance`` or ``gls`` value is supported.
HOST_ID
EBICS specific: name of the EBICS host
@@ -167,6 +167,60 @@ FREQUENCY
IGNORE_TRANSACTIONS_BEFORE
Ignore all transactions before a certain YYYY-MM-DD date, useful when you want to use an existing account with old transactions that should not be bounced.
+HTTP SERVER OPTIONS
+-------------------
+
+The following configuration value(s) belong to the “[nexus-httpd]” section.
+
+SERVE
+ This can either be ``tcp`` or ``unix``.
+
+PORT
+ Port on which the HTTP server listens, e.g. 9967.
+ Only used if ``SERVE`` is ``tcp``.
+
+BIND_TO
+ Which IP address should we bind to? E.g. ``127.0.0.1`` or ``::1``for loopback. Can also be given as a hostname.
+ Only used if ``SERVE`` is ``tcp``.
+
+UNIXPATH
+ Which unix domain path should we bind to?
+ Only used if ``SERVE`` is ``unix``.
+
+UNIXPATH_MODE
+ What should be the file access permissions for ``UNIXPATH``?
+ Only used if ``SERVE`` is ``unix``.
+
+HTTP WIRE GATEWAY API OPTIONS
+-----------------------------
+
+The following configuration value(s) belong to the “[nexus-httpd-wire-gateway-api]” section.
+
+ENABLED
+ Whether to serve the Wire Gateway API.
+
+AUTH_METHOD
+ How to authenticate this API. This can either be ``none`` or ``bearer-token``.
+
+AUTH_BEARER_TOKEN
+ The expected token.
+ Only used if ``AUTH_METHOD`` is ``bearer-token``.
+
+HTTP REVENUE API OPTIONS
+------------------------
+
+The following configuration value(s) belong to the “[nexus-httpd-revenue-api]” section.
+
+ENABLED
+ Whether to serve the Revenue API.
+
+AUTH_METHOD
+ How to authenticate this API. This can either be ``none`` or ``bearer-token``.
+
+AUTH_BEARER_TOKEN
+ The expected token.
+ Only used if ``AUTH_METHOD`` is ``bearer-token``.
+
DATABASE OPTIONS
----------------
diff --git a/manpages/taler.conf.5.rst b/manpages/taler.conf.5.rst
index 3074f68b..ee5d3cd1 100644
--- a/manpages/taler.conf.5.rst
+++ b/manpages/taler.conf.5.rst
@@ -254,19 +254,29 @@ PRIVACY_ETAG
EXCHANGE KYC PROVIDER OPTIONS
-----------------------------
-The following options must be in the section "[kyc-provider-XXX]" sections.
-
-COST
- Relative cost of the KYC provider, non-negative number.
+The following options must be in the section "[kyc-provider-$PROVIDER_NAME]" sections.
LOGIC
API type of the KYC provider.
-USER_TYPE
- Type of user this provider is for, either INDIVIDUAL or BUSINESS.
+CONVERTER
+ Name of a program to run on the output of the plugin
+ to convert the result into the desired set of attributes.
+ The converter must create a log for the system administrator
+ if the provided inputs do not match expectations.
+ Note that the converter will be expected to output the
+ set of attributes listed under the respective ``[kyc-check-*]``
+ sections. Calling the converter with ``--list-outputs``
+ should generate a (newline-separated) list of attributes
+ the converter promises to generate in its JSON output
+ (when run regularly).
+
+COST
+ Optional cost, useful if clients want to voluntarily
+ trigger authentication procedures for attestation.
-PROVIDED_CHECKS
- List of checks performed by this provider. Space-separated names of checks, must match check names in legitimization rules.
+Additional logic-specific options may be given in the
+section.
EXCHANGE KYC OAUTH2 OPTIONS
@@ -362,6 +372,169 @@ WEBHOOK_AUTH_TOKEN
Authentication token Persona must supply to our webhook. This is an optional setting.
+EXCHANGE KYC CHECK OPTIONS
+--------------------------
+
+The following options must be in "[kyc-check-$CHECK_NAME]" sections.
+
+TYPE
+ Which type of check is this? Also determines
+ the SPA form to show to the user for this check.
+
+ * INFO: wait for staff or contact staff out-of band
+ (only information shown, no SPA action)
+ * FORM: SPA should show an inline (HTML) form
+ * LINK: SPA may start external KYC process or upload
+
+VOLUNTARY
+ Optional. Set to YES to allow this check be
+ done voluntarily by a client (they may then
+ still have to pay for it). Used to offer the
+ SPA to display checks even if they are
+ not required. Default is NO.
+
+PROVIDER_ID
+ Provider id, present only if type is LINK.
+ Refers to a ``kyc-provider-$PROVIDER_ID`` section.
+
+FORM_NAME
+ Name of the SPA form, if type is FORM
+ "INFO" and "LINK" are reserved and must not be used.
+ The exchange server and the SPA must agree on a list
+ of supported forms and the resulting attributes.
+ The SPA should include a JSON resource file
+ "forms.json" mapping form names to arrays of
+ attribute names each form provides.
+ The list of possible FORM names is fixed in the SPA
+ for a particular exchange release.
+
+DESCRIPTION
+ Descriptions to use in the SPA to display the check.
+
+DESCRIPTION_I18N
+ JSON with internationalized descriptions to use
+ in the SPA to display the check.
+
+REQUIRES
+ ';'-separated list of fields that the CONTEXT must
+ provide as inputs to this check. For example,
+ for a FORM of type CHOICE, this might state
+ ``choices: string[];``. The type after the ":"
+ is for now purely for documentation and is
+ not checked. However, it may be shown to AML staff
+ when they configure measures.
+
+OUTPUTS = business_name street city country registration
+ Description of the outputs provided by the check.
+ Basically, the check's output is expected to
+ provide the following fields as attribute inputs into
+ a subsequent AML program.
+ Only given for type FORM; INFO never has any outputs,
+ and for type LINK we can obtain the same information
+ from the CONVERTER via ``--list-outputs``.
+
+FALLBACK
+ Name of an **original** measure to take if the check fails
+ (for any reason, e.g. provider or form fail to
+ satisfy constraints or provider signals user error)
+ Usually should point to a measure that requests
+ AML staff to investigate. The fallback measure
+ context always includes the reasons for the
+ failure.
+
+EXCHANGE KYC RULES
+------------------
+
+The following options must be in "[kyc-rule-$RULE_NAME]" sections.
+
+OPERATION_TYPE = WITHDRAW
+ Operation that triggers this rule.
+ Must be one of WITHDRAW, DEPOSIT, P2P-RECEIVE
+ or WALLET-BALANCE.
+
+NEXT_MEASURES
+ Space-separated list of next measures to be performed.
+ The SPA should display *all* of these measures to the user.
+ (They have a choice of either which ones, or in
+ which order they are to be performed.)
+ A special measure name "verboten" is used if the
+ specified threshold may never be crossed
+ (under this set of rules).
+
+IS_AND_COMBINATOR
+ "YES" if all NEXT_MEASURES will eventually need
+ to be satisfied, "NO" the user has a choice between
+ them. Not actually enforced by the exchange, but
+ primarily used to inform the user whether this is
+ an "and" or "or". YES for "and".
+
+EXPOSED
+ YES if the rule (specifically, operation type,
+ threshold, timeframe) and the general nature of
+ the next measure (verboten or approval required)
+ should be exposed to the client.
+ Defaults to NO if not set.
+
+THRESHOLD
+ Threshold amount above which the rule is
+ triggered. The total must be exceeded in the given
+ timeframe.
+
+TIMEFRAME
+ Timeframe over which the amount to be compared to
+ the THRESHOLD is calculated (for example, "30 days").
+ Ignored for WALLET-BALANCE. Can be 'forever'.
+
+ENABLED = NO
+ Set to YES to enable the rule (default is NO).
+
+
+EXCHANGE AML PROGRAMS
+---------------------
+
+The following options must be in "[aml-program-$PROG_NAME]" sections.
+
+COMMAND
+ Name of the program to run. Must match a binary
+ on the local machine where the exchange is running.
+
+DESCRIPTION
+ Human-readable description of what this
+ AML helper program will do. Used to show
+ to the AML staff.
+
+ENABLED
+ True if this AML program is enabled (and thus can be
+ used in measures and exposed to AML staff).
+ Optional, default is NO.
+
+FALLBACK
+ Name of an **original** measure to take if COMMAND fails
+ Usually points to a measure that asks AML staff
+ to contact the systems administrator. The fallback measure
+ context always includes the reasons for the
+ failure.
+
+EXCHANGE KYC MEASURES
+---------------------
+
+The following options must be in "[kyc-measure-$MEASURE_NAME]" sections. These sections define the **original** measures.
+
+CHECK_NAME
+ Name of a possible check for this measure. Optional.
+ If not given, PROGRAM should be run immediately
+ (on an empty set of attributes).
+
+CONTEXT = {"choices":["individual","business"]}
+ Context for the check. The context can be
+ just an empty JSON object if there is none.
+
+PROGRAM
+ Program to run on the context and check data to
+ determine the outcome and next measure.
+ Refers to a ``[aml-program-$PROG_NAME]`` section name.
+
+
EXCHANGE EXTENSIONS OPTIONS
---------------------------
diff --git a/python-guidelines.rst b/orphaned/python-guidelines.rst
index 8a644ced..8a644ced 100644
--- a/python-guidelines.rst
+++ b/orphaned/python-guidelines.rst
diff --git a/checklists/qa-0.9.4.rst b/orphaned/qa-0.9.4.rst
index 77e51081..77e51081 100644
--- a/checklists/qa-0.9.4.rst
+++ b/orphaned/qa-0.9.4.rst
diff --git a/screenshots/cta-balance-list-android-1.png b/screenshots/cta-balance-list-android-1.png
new file mode 100644
index 00000000..ea552f77
--- /dev/null
+++ b/screenshots/cta-balance-list-android-1.png
Binary files differ
diff --git a/screenshots/cta-balance-list-android-latest.png b/screenshots/cta-balance-list-android-latest.png
index 90d09afe..6811c9de 120000
--- a/screenshots/cta-balance-list-android-latest.png
+++ b/screenshots/cta-balance-list-android-latest.png
@@ -1 +1 @@
-cta-balance-list-android-0.png \ No newline at end of file
+cta-balance-list-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-payment-paid-android-1.png b/screenshots/cta-payment-paid-android-1.png
new file mode 100644
index 00000000..af6c5633
--- /dev/null
+++ b/screenshots/cta-payment-paid-android-1.png
Binary files differ
diff --git a/screenshots/cta-payment-paid-android-latest.png b/screenshots/cta-payment-paid-android-latest.png
index 8aabd17e..558aa216 120000
--- a/screenshots/cta-payment-paid-android-latest.png
+++ b/screenshots/cta-payment-paid-android-latest.png
@@ -1 +1 @@
-cta-payment-paid-android-0.png \ No newline at end of file
+cta-payment-paid-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-peer-pull-initiate-android-1.png b/screenshots/cta-peer-pull-initiate-android-1.png
new file mode 100644
index 00000000..4109253c
--- /dev/null
+++ b/screenshots/cta-peer-pull-initiate-android-1.png
Binary files differ
diff --git a/screenshots/cta-peer-pull-initiate-android-latest.png b/screenshots/cta-peer-pull-initiate-android-latest.png
index eeb4d8c9..37cf7434 120000
--- a/screenshots/cta-peer-pull-initiate-android-latest.png
+++ b/screenshots/cta-peer-pull-initiate-android-latest.png
@@ -1 +1 @@
-cta-peer-pull-initiate-android-0.png \ No newline at end of file
+cta-peer-pull-initiate-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-transaction-list-android-1.png b/screenshots/cta-transaction-list-android-1.png
new file mode 100644
index 00000000..c3ac2e4f
--- /dev/null
+++ b/screenshots/cta-transaction-list-android-1.png
Binary files differ
diff --git a/screenshots/cta-transaction-list-android-latest.png b/screenshots/cta-transaction-list-android-latest.png
index 9bf9ffa8..f5c83c2c 120000
--- a/screenshots/cta-transaction-list-android-latest.png
+++ b/screenshots/cta-transaction-list-android-latest.png
@@ -1 +1 @@
-cta-transaction-list-android-0.png \ No newline at end of file
+cta-transaction-list-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-url-entry-android-1.png b/screenshots/cta-url-entry-android-1.png
new file mode 100644
index 00000000..4032bf5d
--- /dev/null
+++ b/screenshots/cta-url-entry-android-1.png
Binary files differ
diff --git a/screenshots/cta-url-entry-android-latest.png b/screenshots/cta-url-entry-android-latest.png
index 2cd152f8..053842c6 120000
--- a/screenshots/cta-url-entry-android-latest.png
+++ b/screenshots/cta-url-entry-android-latest.png
@@ -1 +1 @@
-cta-url-entry-android-0.png \ No newline at end of file
+cta-url-entry-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-wire-transfer-android-1.png b/screenshots/cta-wire-transfer-android-1.png
new file mode 100644
index 00000000..0dd0e4b0
--- /dev/null
+++ b/screenshots/cta-wire-transfer-android-1.png
Binary files differ
diff --git a/screenshots/cta-wire-transfer-android-latest.png b/screenshots/cta-wire-transfer-android-latest.png
index f50509ba..38074199 120000
--- a/screenshots/cta-wire-transfer-android-latest.png
+++ b/screenshots/cta-wire-transfer-android-latest.png
@@ -1 +1 @@
-cta-wire-transfer-android-0.png \ No newline at end of file
+cta-wire-transfer-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-confirm-android-latest.png b/screenshots/cta-withdraw-confirm-android-latest.png
new file mode 120000
index 00000000..2df9752e
--- /dev/null
+++ b/screenshots/cta-withdraw-confirm-android-latest.png
@@ -0,0 +1 @@
+cta-withdraw-confirm-android-0.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-confirm-chrome-latest.png b/screenshots/cta-withdraw-confirm-chrome-latest.png
new file mode 120000
index 00000000..be4894fe
--- /dev/null
+++ b/screenshots/cta-withdraw-confirm-chrome-latest.png
@@ -0,0 +1 @@
+cta-withdraw-confirm-chrome-0.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-confirm-firefox-latest.png b/screenshots/cta-withdraw-confirm-firefox-latest.png
new file mode 120000
index 00000000..2e4531fc
--- /dev/null
+++ b/screenshots/cta-withdraw-confirm-firefox-latest.png
@@ -0,0 +1 @@
+cta-withdraw-confirm-firefox-0.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-confirm-ios-latest.png b/screenshots/cta-withdraw-confirm-ios-latest.png
new file mode 120000
index 00000000..665fddba
--- /dev/null
+++ b/screenshots/cta-withdraw-confirm-ios-latest.png
@@ -0,0 +1 @@
+cta-withdraw-confirm-ios-0.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-done-android-1.png b/screenshots/cta-withdraw-done-android-1.png
new file mode 100644
index 00000000..11fc66c5
--- /dev/null
+++ b/screenshots/cta-withdraw-done-android-1.png
Binary files differ
diff --git a/screenshots/cta-withdraw-done-android-latest.png b/screenshots/cta-withdraw-done-android-latest.png
index 7751f9d0..78f631b2 120000
--- a/screenshots/cta-withdraw-done-android-latest.png
+++ b/screenshots/cta-withdraw-done-android-latest.png
@@ -1 +1 @@
-cta-withdraw-done-android-0.png \ No newline at end of file
+cta-withdraw-done-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-firefox-latest.png b/screenshots/cta-withdraw-firefox-latest.png
new file mode 120000
index 00000000..4ed704c3
--- /dev/null
+++ b/screenshots/cta-withdraw-firefox-latest.png
@@ -0,0 +1 @@
+cta-withdraw-firefox-0.png \ No newline at end of file
diff --git a/screenshots/dynamic-forms.amount.png b/screenshots/dynamic-forms.amount.png
new file mode 100644
index 00000000..854d901c
--- /dev/null
+++ b/screenshots/dynamic-forms.amount.png
Binary files differ
diff --git a/screenshots/dynamic-forms.boolean.png b/screenshots/dynamic-forms.boolean.png
new file mode 100644
index 00000000..92b1e3bd
--- /dev/null
+++ b/screenshots/dynamic-forms.boolean.png
Binary files differ
diff --git a/screenshots/dynamic-forms.choice.png b/screenshots/dynamic-forms.choice.png
new file mode 100644
index 00000000..5922322f
--- /dev/null
+++ b/screenshots/dynamic-forms.choice.png
Binary files differ
diff --git a/screenshots/dynamic-forms.decorative-elements.png b/screenshots/dynamic-forms.decorative-elements.png
new file mode 100644
index 00000000..4981b2e9
--- /dev/null
+++ b/screenshots/dynamic-forms.decorative-elements.png
Binary files differ
diff --git a/screenshots/dynamic-forms.file.png b/screenshots/dynamic-forms.file.png
new file mode 100644
index 00000000..0895b353
--- /dev/null
+++ b/screenshots/dynamic-forms.file.png
Binary files differ
diff --git a/screenshots/dynamic-forms.list.png b/screenshots/dynamic-forms.list.png
new file mode 100644
index 00000000..0f007254
--- /dev/null
+++ b/screenshots/dynamic-forms.list.png
Binary files differ
diff --git a/screenshots/dynamic-forms.time.png b/screenshots/dynamic-forms.time.png
new file mode 100644
index 00000000..00c8c57c
--- /dev/null
+++ b/screenshots/dynamic-forms.time.png
Binary files differ
diff --git a/images/grafana-postgres-exporter.png b/system-administration/images/grafana-postgres-exporter.png
index a51c28f0..a51c28f0 100644
--- a/images/grafana-postgres-exporter.png
+++ b/system-administration/images/grafana-postgres-exporter.png
Binary files differ
diff --git a/images/kuma.png b/system-administration/images/kuma.png
index d98772a1..d98772a1 100644
--- a/images/kuma.png
+++ b/system-administration/images/kuma.png
Binary files differ
diff --git a/system-administration/images/lego-logo.svg b/system-administration/images/lego-logo.svg
new file mode 100644
index 00000000..2b578d34
--- /dev/null
+++ b/system-administration/images/lego-logo.svg
@@ -0,0 +1 @@
+<svg width="538.167" height="152.232" viewBox="0 0 142.39 40.278" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" stroke="#00add8" stroke-width="2.646"><path d="M129.04 6.615c-6.952 0-6.952 4.973-6.952 6.024V27.61c0 .62 0 6.053 6.952 6.053s6.735-5.423 6.735-6.053V12.64c0-1.013.217-6.024-6.735-6.024z"/><path d="M113.61 12.639c0-1.013.217-6.025-6.735-6.025s-6.952 4.973-6.952 6.025V27.61c0 .62 0 6.053 6.952 6.053s6.735-5.423 6.735-6.053v-7.465h-4.53" stroke-linecap="square"/></g><g fill="none" stroke="#00add8" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.323"><path d="M88.866 31.356v-3.374c0-1.472-.874-2.83-2.724-2.83H81.05m5.509 8.511a2.307 2.307 0 1 0 4.614 0 2.307 2.307 0 0 0-4.614 0zM86.559 20.145h-5.551m5.551 0a2.307 2.307 0 1 0 4.614 0 2.307 2.307 0 0 0-4.614 0zM88.866 8.922v3.374c0 1.472-.874 2.83-2.724 2.83H81.05m5.509-8.511a2.307 2.307 0 1 0 4.614 0 2.307 2.307 0 0 0-4.614 0z"/></g><path d="M62.737 13.728V9.291c-.001-3.22 2.772-5.887 5.993-5.889 3.221-.002 5.997 2.662 6 5.883.002 3.22 0 4.443 0 4.443" fill="none" stroke="#4db969" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.133" style="paint-order:fill markers stroke"/><rect x="60.158" y="13.728" width="17.047" height="12.13" ry="1.725" fill="#4db969" stroke="#4db969" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.117" style="paint-order:normal"/><g fill="#fff" stroke-width=".146"><path class="cls-4" d="M66.397 21.903a.414.414 0 0 0 .358-.206l.358-.62.285-.494.015-.025.906-1.571a.414.414 0 0 1 .717 0l.61 1.055a.412.412 0 1 0 .716-.412l-1.326-2.297a.414.414 0 0 0-.717 0l-2.28 3.947a.414.414 0 0 0 .358.623z"/><path class="cls-4" d="M73.172 22.73h-8.207a.414.414 0 0 1-.358-.62l3.713-6.432a.414.414 0 0 1 .716 0l2.759 4.774a.414.414 0 0 1-.358.62h-3.129a.412.412 0 1 0 0 .826h4.563a.414.414 0 0 0 .358-.62l-3.865-6.695a.414.414 0 0 0-.358-.208h-.652a.414.414 0 0 0-.359.208l-4.492 7.781a.411.411 0 0 0 0 .414l.326.564a.41.41 0 0 0 .357.207h8.987a.41.41 0 0 0 .357-.207.412.412 0 0 0-.358-.612zM73.226 19.629l.868 1.503a.412.412 0 1 0 .715-.414l-.868-1.501a.414.414 0 0 0-.715.412zM70.555 15.003l.284.491a.412.412 0 1 0 .715-.412l-.283-.49a.414.414 0 0 0-.716.411zM71.793 17.147l.478.829a.414.414 0 0 0 .716-.414l-.478-.829a.414.414 0 0 0-.716.414zM72.217 24.384h-.981a.414.414 0 0 0 0 .827h.98a.412.412 0 0 0 .357-.62.413.413 0 0 0-.356-.207zM69.327 24.384a.414.414 0 1 0 .001.828.414.414 0 0 0-.001-.828zM65.564 17.146l1.237-2.143a.414.414 0 0 0-.717-.412l-1.236 2.141a.414.414 0 1 0 .716.414zM63.269 21.132l1.346-2.332a.412.412 0 1 0-.715-.414l-1.346 2.332a.412.412 0 1 0 .715.414zM67.418 24.384h-2.28a.414.414 0 0 0 .002.827h2.278a.415.415 0 0 0 .358-.62.415.415 0 0 0-.358-.207z"/></g><g fill="none" stroke="#f9a11d" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.323"><path d="M48.523 31.356v-3.374c0-1.472.874-2.83 2.724-2.83h5.092m-5.509 8.511a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0zM50.83 20.145h5.551m-5.551 0a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0zM48.523 8.922v3.374c0 1.472.874 2.83 2.724 2.83h5.092M50.83 6.614a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0z"/></g><g fill="none" stroke="#f9a11d" stroke-linecap="square"><path d="M34.821 20.145H24.104m13.285 13.518H24.104V6.614h13.285" stroke-width="2.646"/><path d="M6.615 33.663h10.9M6.615 6.614v27.049m0-27.049v27.049" stroke-width="2.381"/></g></svg>
diff --git a/images/taler-monitoring-infrastructure.png b/system-administration/images/taler-monitoring-infrastructure.png
index 05f29704..05f29704 100644
--- a/images/taler-monitoring-infrastructure.png
+++ b/system-administration/images/taler-monitoring-infrastructure.png
Binary files differ
diff --git a/images/uptime-kuma-edit.png b/system-administration/images/uptime-kuma-edit.png
index 23b85dad..23b85dad 100644
--- a/images/uptime-kuma-edit.png
+++ b/system-administration/images/uptime-kuma-edit.png
Binary files differ
diff --git a/images/uptime-kuma-from-grafana.png b/system-administration/images/uptime-kuma-from-grafana.png
index c42b8660..c42b8660 100644
--- a/images/uptime-kuma-from-grafana.png
+++ b/system-administration/images/uptime-kuma-from-grafana.png
Binary files differ
diff --git a/system-administration/index.rst b/system-administration/index.rst
new file mode 100644
index 00000000..e573f7c8
--- /dev/null
+++ b/system-administration/index.rst
@@ -0,0 +1,26 @@
+..
+ 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 Javier Sepulveda
+
+System Administration tutorials
+##################################
+
+.. toctree::
+ :maxdepth: 1
+ :glob:
+
+ lego-certificates
+ taler-monitoring-infrastructure
diff --git a/system-administration/lego-certificates.rst b/system-administration/lego-certificates.rst
new file mode 100644
index 00000000..ebc35329
--- /dev/null
+++ b/system-administration/lego-certificates.rst
@@ -0,0 +1,131 @@
+.. image:: images/lego-logo.svg
+ :width: 300
+ :height: 150
+ :alt: lego logo
+
+What is Lego
+###############
+
+Let's Encrypt client and ACME library written in Go.
+
+* You can request new certificates
+* You can request new subdomain alt names for your current main certicate
+* You can renew certificates
+* You can revoke certificates
+* You can request certificates by using dynamic DNS (API access, with multiple providers)
+
+
+Why lego is better for managing certificates
+===============================================
+
+* The process is not considered a live process, so in case something goes wrong your websites won't break.
+* You can hook some actions after the renewal process, such as reloading Dovecot.
+* The process of either obtaining or renewing new certicates, doesn't require you to stop NGINX.
+* Lego just helps you to obtain the certificates as text files, which you can copy afterwards to the right locations to be used by NGINX.
+
+
+Requirements
+=============
+- A fully automation of installing and deploying Lego can be found in migration-exercise-stable.git/taler.net/lego-certificates
+- If you want to do things manually instead, you can execute the "install-lego.sh" file.
+- To use our script simply execute the "main-certs.sh" file, which not only will install lego on your system, but
+ will try to obtain certificates for the ones listed on the "domains" text file.
+- Lego can work with so many domain providers (dynamic DNS), so please make sure you have indicated the right
+ API credentials on the "envars" variables file for your domain provider. In our specific case, we use Joker.
+- Make sure either you are not using UFW or any firewall program, or that if you are using one, make sure you have opened beforehand
+ the port 80.
+
+Installation and deployment with a script
+#############################################
+
+#. Git clone migration-exercise-stable.git
+#. Navigate to the folder taler.net/lego-certificates
+#. Add your desired FQDNs in the "domains" text file
+#. Execute the "main-certs.sh" file as ./main-certs.sh
+
+Manually installing Lego
+===========================
+
+.. note ::
+ Just as an informative process, as this is fully automated by executing either the "install-lego.sh" or the "main-certs.sh" files.
+
+.. code-block:: console
+
+ $ wget https://github.com/go-acme/lego/releases/download/v4.16.1/lego_v4.16.1_linux_amd64.tar.gz
+ $ tar -axf lego_v4.16.1_linux_amd64.tar.gz
+ $ # If moving directly to /usr/local/bin, just copy the lego binary file to /usr/local/bin
+ $ cp /tmp/lego /usr/local/bin/
+ $ # If copying the binary to /opt/lego, make symbolic links to /usr/local/bin
+ $ cp /tmp/lego /opt/lego/
+ $ ln -s /usr/local/bin /opt/lego/lego
+
+Full documentation on how to use Lego can be found in: https://go-acme.github.io/lego/
+
+Usage of lego once it has been installed
+###############################################
+
+* Each time you want to add an additional domain to your setup, just add the FQDN to the "domains" text file
+* There is nothing else to do in your side now, the server itself will trigger automatically (systemd timer) the "renew-certs.service"
+* We have implemented the use of lego with systemd timers, so there is not additional maintenance
+
+Automatic renewal of certificates
+##################################
+
+We use systemd timers do undertake this.
+
+.. note ::
+ To check the systemd timer is running properly and "waiting", you can execute "systemctl status renew-certs.timer"
+
+More information: https://go-acme.github.io/lego/usage/cli/renew-a-certificate/
+
+
+Email notifications
+====================
+
+* Let's encrypt notifications will arrive to your configured email address.
+* You can specify your email address by editing the "envars" text file (variable "LEGO_ACCOUNT_EMAIL").
+* On each successful renewal, you will receive an email notification from the script.
+
+Additional information for troubleshooting
+###############################################
+
+Once you have the certificate generated files (/root/.lego/xxx.crt, /root/.lego/xxx.key)
+they will be copied to /etc/ssl/certs and /etc/ssl/private, respectively.
+
+How to configure NGINX to use your certificates
+##################################################
+
+In the NGINX virtualhost configuration file just include "include conf.d/talerssl.conf;" line, and
+make sure you have a file named "talerssl.conf" in the path: /etc/nginx/conf.d with the next content:
+
+.. code-block:: console
+
+ $ # Taler SSL defaults
+ $ # We're using one certificate with taler.net as primary name
+ $ # and everything else as alt name.
+ $ # These 2 next lines are the important ones, which refer to the certificates file (.crt), and its private key (.key)
+ $ ssl_certificate /etc/ssl/certs/taler.net.crt;
+ $ ssl_certificate_key /etc/ssl/private/taler.net.key;
+ $ ssl_session_cache shared:SSL:10m;
+ $ ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
+ $ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
+
+
+Presence of Lego in our servers
+######################################
+
+* TUE - University of Eindhoven
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/taler-monitoring-infrastructure.rst b/system-administration/taler-monitoring-infrastructure.rst
index 3b809fb3..e1b26c3b 100644
--- a/taler-monitoring-infrastructure.rst
+++ b/system-administration/taler-monitoring-infrastructure.rst
@@ -24,14 +24,15 @@ GNU Taler monitoring
In order to check the availability of our server infrastructure, we use the Grafana and Uptime KUMA monitoring programs.
-On the one hand Grafana let us to see *graphically* the server consumption resources, and even alert us of some specific situations.
+On the one hand Grafana enables us to see *graphically* the server consumption resources, and even alert us of some specific situations.
On the other hand with a more basic tool such as Uptime KUMA (which does mostly ping and https checks),
-we get the very first status information, as the very first countermeasure.
+we get the very first status information, as a very first countermeasure.
Grafana
=======
- Our grafana instance can be reached at https://grafana.taler.net
+- Our grafana instance is installed on the (TUE) server
User accounts:
--------------
@@ -162,19 +163,22 @@ Grafana Alerting
Uptime Kuma
===========
-- URL: http://139.162.254.179:3001/dashboard
+- URL: https://uptimekuma.anastasis.lu (main)
- Users: One single administration account with full privileges.
-- Installation: With Docker
+- Installation: Without docker. All within the user home folder /home/uptime-kuma
+- Monitors almost all our servers, websites and certificates expiration dates.
+
+- URL: https://uptimekuma.taler.net
+- Users: One single administration account with full privileges.
+- Installation: Without docker. All within the user home folder /home/uptime-kuma
+- Monitors the "main" uptimekuma installation, to make sure it is up and running, and doing the monitoring properly.
.. image:: images/kuma.png
.. note::
- 1) In order to guarantee the KUMA is doing its work, it needs to be install 100% externally from the servers you want to monitor. (Server Kuma 1)
- 2) Also, it is important to monitor the KUMA server itself, so you don't endup without a monitoring system. (Server Kuma 2)
-
-In our case, we do both. We have the two Uptime KUMA servers completely outside our server infrastructure, so one monitors the other, and
-the latter one, monitors our own Taler servers.
+ 1) The main uptimekuma installation is under the server anastasis.lu
+ 2) The second uptimekuma installation on top, is installed on gv.taler.net.
Kuma monitor types
-------------------
@@ -187,9 +191,10 @@ expiration dates.
So in brief in our KUMA main server, we use these 3 monitor types (ping,https,certificate expiration) for each website that we monitor.
-Exceptionally for additional notifications, and specifically due of the importance of the Taler Operations server,
-we use in addition SMS notifications (clicksend provider). This way in case of KUMA detecting the Taler Operations unavailability,
-a SMS message will be sent to at the very least two persons from the deployment and operations department.
+Exceptionally for high priority notifications for essential services, and specifically due of the importance of the Taler Operations production
+server, we use in addition SMS notifications (Clicksend provider). This way in the case the main uptimekuma detecting the Taler Operations server unavailability, or any other essential service such as GIt,
+a SMS message would be sent to the system administrator and eventually some other team member of the deployment and operations department, for urgent action.
+
How to edit notifications:
diff --git a/taler-developer-manual.rst b/taler-developer-manual.rst
index 3035e55d..c225e747 100644
--- a/taler-developer-manual.rst
+++ b/taler-developer-manual.rst
@@ -368,12 +368,6 @@ you can add the nightly package sources.
$ wget -O - https://taler.net/taler-systems-nightly.gpg.key | apt-key add -
-Language-Specific Guidelines
-============================
-
-* :doc:`Python Guidelines <python-guidelines>`
-
-
Taler Deployment on gv.taler.net
================================
@@ -648,7 +642,7 @@ outside of this versioning. All tables of a GNU Taler component should live in
QA Plans
========
-.. include:: checklists/qa-0.9.4.rst
+.. include:: checklists/qa-0.10.rst
Releases
diff --git a/taler-exchange-manual.rst b/taler-exchange-manual.rst
index 91865f24..069b256c 100644
--- a/taler-exchange-manual.rst
+++ b/taler-exchange-manual.rst
@@ -1023,6 +1023,20 @@ Taler KYC Terminology
*cost*. Interaction with a provider is performed by provider-specific
*logic*.
+Configuration of possible KYC/AML checks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The configuration specifies a set of possible KYC checks offered by external
+providers. The names of the configuration sections must being with
+``kyc-check-`` followed by an arbitrary ``$CHECK_NAME``.
+
+The list of possible FORM names is fixed in the SPA
+for a particular exchange release.
+
+The outcome of *any* check should always be uploaded encrypted into the
+``kyc_attributes`` table. It MUST include an ``expiration_time``.
+
+
KYC Configuration Options
-------------------------
@@ -1724,6 +1738,155 @@ from ``https://git.taler.net/wallet-core.git/``, compile and copy the file
from the ``dist/prod``.
+AML Programs
+------------
+
+AML programs are helper programs that can:
+
+* Generate a list of *required* context field names
+ for the helper (introspection!) using the "--required-context"
+ command-line switch. The output should use the same
+ syntax as the REQUIRES clause of ``[kyc-check-]``
+ configuration sections, except that new lines
+ MUST be used to separate fields instead of ";".
+* Generate a list of *required* attribute names
+ for the helper (introspection!) using the "--required-attributes"
+ command-line switch. The output should use the same
+ list of names as the ATTRIBUTES in the
+ ``[kyc-provider-]`` configuration section
+ (but may also include FORM field names).
+* Process an input JSON object of type
+ `AmlProgramInput` into a JSON object of
+ type `AmlOutcome`.
+ This is the default behavior if no command-line switches
+ are provided.
+
+.. ts:def:: AmlProgramInput
+
+ interface AmlProgramInput {
+
+ // JSON object that was provided as
+ // part of the *measure*. This JSON object is
+ // provided under "context" in the main JSON object
+ // input to the AML program. This "context" should
+ // satify both the REQUIRES clause of the respective
+ // check and the output of "--requires" from the
+ // AML program's command-line option.
+ context?: Object;
+
+ // JSON object that captures the
+ // output of a ``[kyc-provider-]`` or (HTML) FORM.
+ // The keys in the JSON object will be the attribute
+ // names and the values must be strings representing
+ // the data. In the case of file uploads, the data
+ // MUST be base64-encoded.
+ attributes: Object;
+
+ // JSON array with the results of historic
+ // AML desisions about the account.
+ aml_history: AmlDecisionDetail[];
+
+ // JSON array with the results of historic
+ // KYC data about the account.
+ kyc_history: KycDetail[];
+
+ }
+
+.. ts:def:: AmlOutcome
+
+ interface AmlOutcome {
+
+ // Should the client's account be investigated
+ // by AML staff?
+ // Defaults to false.
+ to_investigate?: boolean;
+
+ // Free-form properties about the account.
+ // Can be used to store properties such as PEP,
+ // risk category, type of business, hits on
+ // sanctions lists, etc.
+ properties?: AccountProperties;
+
+ // Types of events to add to the KYC events table.
+ // (for statistics).
+ events?: string[];
+
+ // KYC rules to apply. Note that this
+ // overrides *all* of the default rules
+ // until the ``expiration_time`` and specifies
+ // the successor measure to apply after the
+ // expiration time.
+ new_rules: LegitimizationRuleSet;
+
+ }
+
+If the AML program fails (exits with a failure code or
+does not provide well-formed JSON output) the AML/KYC
+process continues with the FALLBACK measure. This should
+usually be one that asks AML staff to contact the
+systems administrator.
+
+AML programs are listed in the configuration file, one program per section:
+
+.. code-block:: ini
+
+ [aml-program-$PROG_NAME]
+
+ # Program to run.
+ COMMAND = taler-helper-aml-pep
+
+ # Human-readable description of what this
+ # AML helper program will do. Used to show
+ # to the AML staff.
+ DESCRIPTION = "check if the customer is a PEP"
+
+ # True if this AML program is enabled (and thus can be
+ # used in measures and exposed to AML staff).
+ # Optional, default is NO.
+ ENABLED = YES
+
+ # **original** measure to take if COMMAND fails
+ # Usually points to a measure that asks AML staff
+ # to contact the systems administrator. The fallback measure
+ # context always includes the reasons for the
+ # failure.
+ FALLBACK = MEASURE_NAME
+
+AML Measures
+------------
+
+The exchange configuration specifies a set of
+**original** *measures* one per configuration section:
+
+.. code-block:: ini
+
+ [kyc-measure-$MEASURE_NAME]
+
+ # Possible check for this measure. Optional.
+ # If not given, PROGRAM should be run immediately
+ # (on an empty set of attributes).
+ CHECK_NAME = IB_FORM
+
+ # Context for the check. The context can be
+ # just an empty JSON object if there is none.
+ CONTEXT = {"choices":["individual","business"]}
+
+ # Program to run on the context and check data to
+ # determine the outcome and next measure.
+ PROGRAM = taler-aml-program
+
+If no ``CHECK_NAME`` is provided at all, the AML ``PROGRAM`` is to be run
+immediately. This is useful if no client-interaction is required to arrive at
+a decision.
+
+.. note::
+
+ The list of *measures* is not complete: AML staff may freely define new
+ measures dynamically, usually by selecting checks, an AML program, and
+ providing context.
+
+
+
Setup Linting
=============
@@ -1902,6 +2065,22 @@ The database scheme used by the exchange looks as follows:
.. image:: images/exchange-db.png
+The ``jmeasures`` JSON in the ``legitimization_measures``
+table is of type `LegitimizationMeasures`:
+
+.. ts:def:: LegitimizationMeasures
+
+ interface LegitimizationMeasures {
+
+ // Array of legitimization measures that
+ // are to be applied.
+ measures: MeasureInformation[];
+
+ // True if the client is expected to eventually satisfy all requirements.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean;
+ }
+
.. _Database-upgrades:
diff --git a/taler-merchant-manual.rst b/taler-merchant-manual.rst
index 353885cd..c647d3a2 100644
--- a/taler-merchant-manual.rst
+++ b/taler-merchant-manual.rst
@@ -635,11 +635,9 @@ section, the following options need to be configured:
Note that multiple exchanges can be added to the system by using different
-identifiers in place of ``KUDOS`` in the example above. Note that all of the
-exchanges actually used will use the same currency: If the currency does not
-match the main ``CURRENCY`` option from the ``taler`` section, the respective
-``merchant-exchange-`` section is automatically ignored. If you need support
-for multiple currencies, you need to deploy one backend per currency.
+identifiers in place of ``KUDOS`` in the example above. One exchange will only
+ever support a single currency; thus, if you need support for multiple
+currencies, you must add multiple exchanges.
The merchant already ships with a default configuration that contains the
``merchant-exchange-kudos`` section from above.
diff --git a/taler-merchant-pos-terminal.rst b/taler-merchant-pos-terminal.rst
index efc487cb..50e7ff97 100644
--- a/taler-merchant-pos-terminal.rst
+++ b/taler-merchant-pos-terminal.rst
@@ -106,19 +106,6 @@ The elements of the JSON file are defined as follows:
api_key: string;
}
- .. ts:def:: MerchantCategory
-
- interface MerchantCategory {
- // A unique numeric ID of the category
- id: number;
-
- // The name of the category. This will be shown to users and used in the order summary.
- name: string;
-
- // Map from IETF BCP 47 language tags to localized names
- name_i18n?: { [lang_tag: string]: string };
- }
-
.. ts:def:: MerchantProduct
diff --git a/wallet/wallet-core.md b/wallet/wallet-core.md
index 8c600081..56388f82 100644
--- a/wallet/wallet-core.md
+++ b/wallet/wallet-core.md
@@ -3,8 +3,10 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
## Overview
### Unknown Group
* [InitWalletOp](#initwalletop)
+* [ShutdownOp](#shutdownop)
* [SetWalletRunConfigOp](#setwalletrunconfigop)
* [GetVersionOp](#getversionop)
+* [HintNetworkAvailabilityOp](#hintnetworkavailabilityop)
### Basic Wallet Information
* [GetBalancesOp](#getbalancesop)
* [GetBalancesDetailOp](#getbalancesdetailop)
@@ -30,11 +32,14 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
### Withdrawals
* [GetWithdrawalDetailsForAmountOp](#getwithdrawaldetailsforamountop)
* [GetWithdrawalDetailsForUriOp](#getwithdrawaldetailsforuriop)
+* [PrepareBankIntegratedWithdrawalOp](#preparebankintegratedwithdrawalop)
+* [ConfirmWithdrawalOp](#confirmwithdrawalop)
* [AcceptBankIntegratedWithdrawalOp](#acceptbankintegratedwithdrawalop)
* [AcceptManualWithdrawalOp](#acceptmanualwithdrawalop)
### Merchant Payments
* [PreparePayForUriOp](#preparepayforuriop)
* [SharePaymentOp](#sharepaymentop)
+* [CheckPayForTemplateOp](#checkpayfortemplateop)
* [PreparePayForTemplateOp](#preparepayfortemplateop)
* [GetContractTermsDetailsOp](#getcontracttermsdetailsop)
* [ConfirmPayOp](#confirmpayop)
@@ -92,6 +97,7 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
* [ConfirmPeerPullDebitOp](#confirmpeerpulldebitop)
### Data Validation
* [ValidateIbanOp](#validateibanop)
+* [CanonicalizeBaseUrlOp](#canonicalizebaseurlop)
### Database Management
* [ExportDbOp](#exportdbop)
* [ImportDbOp](#importdbop)
@@ -111,9 +117,11 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
* [TestingSetTimetravelOp](#testingsettimetravelop)
* [TestingListTasksForTransactionOp](#testinglisttasksfortransactionop)
* [TestingWaitTransactionsFinalOp](#testingwaittransactionsfinalop)
+* [TestingWaitTasksDoneOp](#testingwaittasksdoneop)
* [TestingWaitRefreshesFinalOp](#testingwaitrefreshesfinalop)
* [TestingWaitTransactionStateOp](#testingwaittransactionstateop)
* [TestingPingOp](#testingpingop)
+* [TestingGetReserveHistoryOp](#testinggetreservehistoryop)
* [TestingGetDenomStatsOp](#testinggetdenomstatsop)
* [SetCoinSuspendedOp](#setcoinsuspendedop)
* [ForceRefreshOp](#forcerefreshop)
@@ -134,6 +142,17 @@ export type InitWalletOp = {
```
+### ShutdownOp
+```typescript
+export type ShutdownOp = {
+ op: WalletApiOperation.Shutdown;
+ request: EmptyObject;
+ response: EmptyObject;
+};
+// Shutdown = "shutdown"
+
+```
+
### SetWalletRunConfigOp
```typescript
/**
@@ -161,6 +180,23 @@ export type GetVersionOp = {
```
+### HintNetworkAvailabilityOp
+```typescript
+export type HintNetworkAvailabilityOp = {
+ op: WalletApiOperation.HintNetworkAvailability;
+ request: HintNetworkAvailabilityRequest;
+ response: EmptyObject;
+};
+// HintNetworkAvailability = "hintNetworkAvailability"
+
+```
+```typescript
+export interface HintNetworkAvailabilityRequest {
+ isNetworkAvailable: boolean;
+}
+
+```
+
### GetBalancesOp
```typescript
/**
@@ -645,6 +681,18 @@ export interface GetWithdrawalDetailsForAmountRequest {
exchangeBaseUrl: string;
amount: AmountString;
restrictAge?: number;
+ /**
+ * ID provided by the client to cancel the request.
+ *
+ * If the same request is made again with the same clientCancellationId,
+ * all previous requests are cancelled.
+ *
+ * The cancelled request will receive an error response with
+ * an error code that indicates the cancellation.
+ *
+ * The cancellation is best-effort, responses might still arrive.
+ */
+ clientCancellationId?: string;
}
```
@@ -711,7 +759,6 @@ export type GetWithdrawalDetailsForUriOp = {
export interface GetWithdrawalDetailsForUriRequest {
talerWithdrawUri: string;
restrictAge?: number;
- notifyChangeFromPendingTimeoutMs?: number;
}
```
@@ -735,10 +782,61 @@ export type WithdrawalOperationStatus =
```
+### PrepareBankIntegratedWithdrawalOp
+```typescript
+/**
+ * Prepare a bank-integrated withdrawal operation.
+ */
+export type PrepareBankIntegratedWithdrawalOp = {
+ op: WalletApiOperation.PrepareBankIntegratedWithdrawal;
+ request: PrepareBankIntegratedWithdrawalRequest;
+ response: PrepareBankIntegratedWithdrawalResponse;
+};
+// PrepareBankIntegratedWithdrawal = "prepareBankIntegratedWithdrawal"
+
+```
+```typescript
+export interface PrepareBankIntegratedWithdrawalRequest {
+ talerWithdrawUri: string;
+ exchangeBaseUrl: string;
+ forcedDenomSel?: ForcedDenomSel;
+ restrictAge?: number;
+}
+
+```
+```typescript
+export interface PrepareBankIntegratedWithdrawalResponse {
+ transactionId: string;
+}
+
+```
+
+### ConfirmWithdrawalOp
+```typescript
+/**
+ * Confirm a withdrawal transaction.
+ */
+export type ConfirmWithdrawalOp = {
+ op: WalletApiOperation.ConfirmWithdrawal;
+ request: ConfirmWithdrawalRequest;
+ response: EmptyObject;
+};
+// ConfirmWithdrawal = "confirmWithdrawal"
+
+```
+```typescript
+export interface ConfirmWithdrawalRequest {
+ transactionId: string;
+}
+
+```
+
### AcceptBankIntegratedWithdrawalOp
```typescript
/**
* Accept a bank-integrated withdrawal.
+ *
+ * @deprecated in favor of prepare/confirm withdrawal.
*/
export type AcceptBankIntegratedWithdrawalOp = {
op: WalletApiOperation.AcceptBankIntegratedWithdrawal;
@@ -784,6 +882,14 @@ export interface AcceptManualWithdrawalRequest {
exchangeBaseUrl: string;
amount: AmountString;
restrictAge?: number;
+ /**
+ * Instead of generating a fresh, random reserve key pair,
+ * use the provided reserve private key.
+ *
+ * Use with caution. Usage of this field may be restricted
+ * to developer mode.
+ */
+ forceReservePriv?: EddsaPrivateKeyString;
}
```
@@ -849,6 +955,53 @@ export interface SharePaymentResult {
```
+### CheckPayForTemplateOp
+```typescript
+export type CheckPayForTemplateOp = {
+ op: WalletApiOperation.CheckPayForTemplate;
+ request: CheckPayTemplateRequest;
+ response: TalerMerchantApi.WalletTemplateDetails;
+};
+// CheckPayForTemplate = "checkPayForTemplate"
+
+```
+```typescript
+export interface CheckPayTemplateRequest {
+ talerPayTemplateUri: string;
+}
+
+```
+```typescript
+export interface WalletTemplateDetails {
+ template_contract: TemplateContractDetails;
+ editable_defaults?: TemplateContractDetailsDefaults;
+ required_currency?: string;
+}
+
+```
+```typescript
+export interface TemplateContractDetails {
+ summary?: string;
+ currency?: string;
+ amount?: AmountString;
+ minimum_age: Integer;
+ pay_duration: RelativeTime;
+}
+
+```
+```typescript
+export interface TemplateContractDetailsDefaults {
+ summary?: string;
+ currency?: string;
+ /**
+ * Amount *or* a plain currency string.
+ */
+ amount?: string;
+ minimum_age?: Integer;
+}
+
+```
+
### PreparePayForTemplateOp
```typescript
/**
@@ -882,7 +1035,8 @@ export type GetContractTermsDetailsOp = {
```
```typescript
export interface GetContractTermsDetailsRequest {
- proposalId: string;
+ proposalId?: string;
+ transactionId?: string;
}
```
@@ -2374,6 +2528,29 @@ export interface ValidateIbanResponse {
```
+### CanonicalizeBaseUrlOp
+```typescript
+export type CanonicalizeBaseUrlOp = {
+ op: WalletApiOperation.CanonicalizeBaseUrl;
+ request: CanonicalizeBaseUrlRequest;
+ response: CanonicalizeBaseUrlResponse;
+};
+// CanonicalizeBaseUrl = "canonicalizeBaseUrl"
+
+```
+```typescript
+export interface CanonicalizeBaseUrlRequest {
+ url: string;
+}
+
+```
+```typescript
+export interface CanonicalizeBaseUrlResponse {
+ url: string;
+}
+
+```
+
### ExportDbOp
```typescript
/**
@@ -2596,11 +2773,28 @@ export type GetPendingTasksOp = {
export type GetActiveTasksOp = {
op: WalletApiOperation.GetActiveTasks;
request: EmptyObject;
- response: GetActiveTasks;
+ response: GetActiveTasksResponse;
};
// GetActiveTasks = "getActiveTasks"
```
+```typescript
+export interface GetActiveTasksResponse {
+ tasks: ActiveTask[];
+}
+
+```
+```typescript
+export interface ActiveTask {
+ taskId: string;
+ transaction: TransactionIdStr | undefined;
+ firstTry: AbsoluteTime | undefined;
+ nextTry: AbsoluteTime | undefined;
+ retryCounter: number | undefined;
+ lastError: TalerErrorDetail | undefined;
+}
+
+```
### DumpCoinsOp
```typescript
@@ -2819,6 +3013,20 @@ export type TestingWaitTransactionsFinalOp = {
```
+### TestingWaitTasksDoneOp
+```typescript
+/**
+ * Wait until all transactions are in a final state.
+ */
+export type TestingWaitTasksDoneOp = {
+ op: WalletApiOperation.TestingWaitTasksDone;
+ request: EmptyObject;
+ response: EmptyObject;
+};
+// TestingWaitTasksDone = "testingWaitTasksDone"
+
+```
+
### TestingWaitRefreshesFinalOp
```typescript
/**
@@ -2865,6 +3073,17 @@ export type TestingPingOp = {
```
+### TestingGetReserveHistoryOp
+```typescript
+export type TestingGetReserveHistoryOp = {
+ op: WalletApiOperation.TestingGetReserveHistory;
+ request: EmptyObject;
+ response: any;
+};
+// TestingGetReserveHistory = "testingGetReserveHistory"
+
+```
+
### TestingGetDenomStatsOp
```typescript
/**
@@ -2954,6 +3173,7 @@ export interface PartialWalletRunConfig {
builtin?: Partial<WalletRunConfig["builtin"]>;
testing?: Partial<WalletRunConfig["testing"]>;
features?: Partial<WalletRunConfig["features"]>;
+ lazyTaskLoop?: Partial<WalletRunConfig["lazyTaskLoop"]>;
}
```
```typescript
@@ -2987,6 +3207,15 @@ export interface WalletRunConfig {
features: {
allowHttp: boolean;
};
+ /**
+ * Start processing tasks only when explicitly required, even after
+ * init has been called.
+ *
+ * Useful when the wallet is started to make single read-only request,
+ * as otherwise wallet-core starts making network request and process
+ * unrelated pending tasks.
+ */
+ lazyTaskLoop: boolean;
}
```
```typescript
@@ -3032,9 +3261,11 @@ export interface WalletCoreVersion {
export type ScopeInfo = ScopeInfoGlobal | ScopeInfoExchange | ScopeInfoAuditor;
```
```typescript
-export type AmountString = string & {
- [__amount_str]: true;
-};
+export type AmountString =
+ | (string & {
+ [__amount_str]: true;
+ })
+ | LitAmountString;
```
```typescript
/**
@@ -3076,7 +3307,6 @@ export type Transaction =
| TransactionWithdrawal
| TransactionPayment
| TransactionRefund
- | TransactionReward
| TransactionRefresh
| TransactionDeposit
| TransactionPeerPullCredit
@@ -3149,7 +3379,6 @@ export declare enum TransactionType {
Payment = "payment",
Refund = "refund",
Refresh = "refresh",
- Reward = "reward",
Deposit = "deposit",
PeerPushDebit = "peer-push-debit",
PeerPushCredit = "peer-push-credit",
@@ -3243,6 +3472,7 @@ export declare enum TransactionMinorState {
RefundAvailable = "refund-available",
AcceptRefund = "accept-refund",
PaidByOther = "paid-by-other",
+ CompletedByOtherWallet = "completed-by-other-wallet",
}
```
```typescript
@@ -3551,17 +3781,6 @@ export interface RefundPaymentInfo {
}
```
```typescript
-export interface TransactionReward extends TransactionCommon {
- type: TransactionType.Reward;
- amountRaw: AmountString;
- /**
- * More information about the merchant
- */
- amountEffective: AmountString;
- merchantBaseUrl: string;
-}
-```
-```typescript
/**
* A transaction shown for refreshes.
* Only shown for (1) refreshes not associated with other transactions
@@ -4066,7 +4285,6 @@ export interface AddExchangeRequest {
* @deprecated use a separate API call to start a forced exchange update instead
*/
forceUpdate?: boolean;
- masterPub?: string;
}
```
```typescript