summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--anastasis.rst737
-rw-r--r--anastasis_reducer_backup.drawio2
-rw-r--r--anastasis_reducer_backup.pngbin456110 -> 457361 bytes
-rw-r--r--anastasis_reducer_recovery.drawio2
-rw-r--r--anastasis_reducer_recovery.pngbin382200 -> 384322 bytes
-rw-r--r--cf/captcha-payment.txt2
-rw-r--r--core/api-auditor.rst9
-rw-r--r--core/api-bank-access.rst12
-rw-r--r--core/api-bank-integration.rst8
-rw-r--r--core/api-common.rst26
-rw-r--r--core/api-error.rst6
-rw-r--r--core/api-exchange.rst119
-rw-r--r--core/api-merchant.rst1851
-rw-r--r--core/api-sync.rst40
-rw-r--r--core/api-wire.rst42
-rw-r--r--design-documents/002-wallet-exchange-management.rst2
-rw-r--r--design-documents/003-tos-rendering.rst2
-rw-r--r--design-documents/005-wallet-backup-sync.rst6
-rw-r--r--design-documents/007-payment.rst5
-rw-r--r--design-documents/008-fees.rst (renamed from design-documents/fees.rst)6
-rw-r--r--design-documents/009-backup.rst118
-rw-r--r--design-documents/index.rst2
-rw-r--r--developers-manual.rst6
-rw-r--r--libeufin/api-nexus.rst108
-rw-r--r--libeufin/ebics.rst10
-rw-r--r--libeufin/iso20022.rst2
-rw-r--r--manpages/taler-merchant-setup-reserve.1.rst2
-rw-r--r--merchant-benchmark.conf123
-rw-r--r--taler-auditor-manual.rst6
-rw-r--r--taler-bank-manual.rst13
-rw-r--r--taler-exchange-manual.rst4
-rw-r--r--taler-merchant-api-tutorial.rst6
-rw-r--r--taler-merchant-manual.rst525
-rw-r--r--taler-nfc-guide.rst2
34 files changed, 2529 insertions, 1275 deletions
diff --git a/anastasis.rst b/anastasis.rst
index 355d0d7a..e9f63e44 100644
--- a/anastasis.rst
+++ b/anastasis.rst
@@ -312,7 +312,7 @@ Anastasis server operators must be protected against denial-of-service attacks
where an adversary attempts to exhaust operator's resources. The API protects
against these attacks by allowing operators to set fees for all
operations. Furthermore, all data stored comes with an expiration logic, so an
-attacker cannot force servers to store data indefinitively.
+attacker cannot force servers to store data indefinitely.
A second availability issue arises from strong adversaries that may be able to
compute the account keys of some user. While we assume that such an adversary
@@ -481,18 +481,18 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
**Response**:
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The escrow provider responds with an EncryptedRecoveryDocument_ object.
- :status 304 Not modified:
- The client requested the same ressource it already knows.
- :status 400 Bad request:
+ :http:statuscode:`304 Not modified`:
+ The client requested the same resource it already knows.
+ :http:statuscode:`400 Bad request`:
The $ACCOUNT_PUB is not an EdDSA public key.
- :status 402 Payment Required:
+ :http:statuscode:`402 Payment Required`:
The account's balance is too low for the specified operation.
See the Taler payment protocol specification for how to pay.
- :status 403 Forbidden:
+ :http:statuscode:`403 Forbidden`:
The required account signature was invalid.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The requested resource was not found.
*Anastasis-Version*: $NUMBER --- The server must return actual version of the encrypted recovery document via this header.
@@ -551,24 +551,24 @@ In the following, UUID is always defined and used according to `RFC 4122`_.
**Response**:
- :status 204 No Content:
+ :http:statuscode:`204 No content`:
The encrypted recovery document was accepted and stored. "Anastasis-Version" and "Anastasis-UUID" headers
incidate what version and UUID was assigned to this encrypted recovery document upload by the server.
- :status 304 Not modified:
+ :http:statuscode:`304 Not modified`:
The same encrypted recovery document was previously accepted and stored. "Anastasis-Version" header
incidates what version was previously assigned to this encrypted recovery document.
- :status 400 Bad request:
+ :http:statuscode:`400 Bad request`:
The $ACCOUNT_PUB is not an EdDSA public key or mandatory headers are missing.
The response body MUST elaborate on the error using a Taler error code in the typical JSON encoding.
- :status 402 Payment Required:
+ :http:statuscode:`402 Payment required`:
The account's balance is too low for the specified operation.
See the Taler payment protocol specification for how to pay.
The response body MAY provide alternative means for payment.
- :status 403 Forbidden:
+ :http:statuscode:`403 Forbidden`:
The required account signature was invalid. The response body may elaborate on the error.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The *If-Match* Etag does not match the latest prior version known to the server.
- :status 413 Request Entity Too Large:
+ :http:statuscode:`413 Request entity too large`:
The upload is too large *or* too small. The response body may elaborate on the error.
**Details:**
@@ -683,20 +683,20 @@ charge per truth operation using GNU Taler.
Upload a TruthUploadRequest_-Object according to the policy the client created before (see RecoveryDocument_).
If request has been seen before, the server should do nothing, and otherwise store the new object.
- :status 204 No content:
+ :http:statuscode:`204 No content`:
Truth stored successfully.
- :status 304 Not modified:
+ :http:statuscode:`304 Not modified`:
The same truth was previously accepted and stored under this UUID. The
Anastasis server must still update the expiration time for the truth when returning
this response code.
- :status 402 Payment Required:
+ :http:statuscode:`402 Payment required`:
This server requires payment to store truth per item.
See the Taler payment protocol specification for how to pay.
The response body MAY provide alternative means for payment.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The server already has some truth stored under this UUID. The client should check that it
is generating UUIDs with enough entropy.
- :status 412 Precondition Failed:
+ :http:statuscode:`412 Precondition failed`:
The selected authentication method is not supported on this provider.
@@ -744,25 +744,25 @@ charge per truth operation using GNU Taler.
**Response**:
- :status 200 OK:
+ :http:statuscode:`200 OK`:
EncryptedKeyShare_ is returned in body (in binary).
- :status 202 Accepted:
+ :http:statuscode:`202 Accepted`:
The escrow provider will respond out-of-band (i.e. SMS).
The body may contain human-readable instructions on next steps.
- :status 303 See Other:
+ :http:statuscode:`303 See other`:
The provider redirects for authentication (i.e. video identification/WebRTC).
If the client is not a browser, it should launch a browser at the URL
given in the "Location" header and allow the user to re-try the operation
after successful authorization.
- :status 402 Payment Required:
+ :http:statuscode:`402 Payment required`:
The service requires payment for access to truth.
See the Taler payment protocol specification for how to pay.
The response body MAY provide alternative means for payment.
- :status 403 Forbidden:
+ :http:statuscode:`403 Forbidden`:
The server requires a valid "response" to the challenge associated with the UUID.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The server does not know any truth under the given UUID.
- :status 503 Service Unavailable:
+ :http:statuscode:`503 Service Unavailable`:
Server is out of Service.
*Truth-Decryption-Key*: Key used to encrypt the **truth** (see encrypted_truth within TruthUploadRequest_) and which has to provided by the user. The key is stored with
@@ -815,10 +815,79 @@ Anastasis Reducer API
This section describes the Anastasis Reducer API which is used by client applications
to store or load the different states the client application can have.
+The reducer takes a state_ in JSON syntax and returns the new state in JSON syntax.
+
+For example a **state** may take the following structure:
+::
+
+ {
+ "backup_state": "CONTINENT_SELECTING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ]
+ }
+
+The new state depends on the previous one and on the transition action_ with its
+arguments given to the reducer. A **transition argument** also is a statement in JSON syntax:
+::
+
+ {
+ "continent": "Europe"
+ }
+
+The new state returned by the reducer with the state and transition argument defined
+above would look like following for the transition action_ "select_continent":
+::
+
+ {
+ "backup_state": "COUNTRY_SELECTING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ],
+ "selected_continent": "Europe",
+ "countries": [
+ {
+ "code": "ch",
+ "name": "Switzerland",
+ "continent": "Europe",
+ "name_i18n": {
+ "de_DE": "Schweiz",
+ "de_CH": "Schwiiz",
+ "fr": "Suisse",
+ "en": "Swiss"
+ },
+ "currency": "CHF"
+ },
+ {
+ "code": "de",
+ "name": "Germany",
+ "continent": "Europe",
+ "continent_i18n": {
+ "de": "Europa"
+ },
+ "name_i18n": {
+ "de_DE": "Deutschland",
+ "de_CH": "Deutschland",
+ "fr": "Allemagne",
+ "en": "Germany"
+ },
+ "currency": "EUR"
+ }
+ ]
+ }
+
+**State**:
+ - In SELECTING-States the user has to choose one value out of a predefined set of values (for example a continent out of a set of continents).
+ - In COLLECTING-States the user has to give certain values.
+ - In EDITING-States the user is free to choose which values he wants to give.
Backup Reducer
^^^^^^^^^^^^^^
+.. _state:
+.. _action:
.. figure:: anastasis_reducer_backup.png
:name: fig-anastasis_reducer_backup
:alt: fig-anastasis_reducer_backup
@@ -831,6 +900,7 @@ Backup Reducer
The illustration above shows the different states the reducer can have during a backup
process.
+
Recovery Reducer
^^^^^^^^^^^^^^^^
.. figure:: anastasis_reducer_recovery.png
@@ -846,6 +916,619 @@ The illustration above shows the different states the reducer can have during a
process.
+Reducer transitions
+^^^^^^^^^^^^^^^^^^^
+In the following, the individual transitions will be specified in more detail.
+
+
+Initial state
+"""""""""""""
+
+The initial states for backup and recovery processes are looking like following:
+
+**Initial backup state:**
+::
+
+ {
+ "backup_state": "CONTINENT_SELECTING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ]
+ }
+
+
+**Initial backup state:**
+::
+
+ {
+ "recovery_state": "CONTINENT_SELECTING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ]
+ }
+
+
+Common transitions
+""""""""""""""""""
+
+**select_continent:**
+
+Arguments (example):
+::
+
+ {
+ "continent": "Europe"
+ }
+
+Expected new state:
+::
+
+ {
+ "backup_state": "COUNTRY_SELECTING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ],
+ "selected_continent": "Europe",
+ "countries": [
+ {
+ "code": "ch",
+ "name": "Switzerland",
+ "continent": "Europe",
+ "name_i18n": {
+ "de_DE": "Schweiz",
+ "de_CH": "Schwiiz",
+ "fr": "Suisse",
+ "en": "Swiss"
+ },
+ "currency": "CHF"
+ },
+ {
+ "code": "de",
+ "name": "Germany",
+ "continent": "Europe",
+ "continent_i18n": {
+ "de": "Europa"
+ },
+ "name_i18n": {
+ "de_DE": "Deutschland",
+ "de_CH": "Deutschland",
+ "fr": "Allemagne",
+ "en": "Germany"
+ },
+ "currency": "EUR"
+ }
+ ]
+ }
+
+**select_country:**
+
+Arguments (example):
+::
+
+ {
+ "country": "Germany",
+ "country_code": "de",
+ "currency": "EUR"
+ }
+
+Expected new state:
+::
+
+ {
+ "backup_state": "USER_ATTRIBUTES_COLLECTING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ],
+ "selected_continent": "Europe",
+ "countries": [
+ {
+ "code": "ch",
+ "name": "Switzerland",
+ "continent": "Europe",
+ "name_i18n": {
+ "de_DE": "Schweiz",
+ "de_CH": "Schwiiz",
+ "fr": "Suisse",
+ "en": "Swiss"
+ },
+ "currency": "CHF"
+ },
+ {
+ "code": "de",
+ "name": "Germany",
+ "continent": "Europe",
+ "continent_i18n": {
+ "de": "Europa"
+ },
+ "name_i18n": {
+ "de_DE": "Deutschland",
+ "de_CH": "Deutschland",
+ "fr": "Allemagne",
+ "en": "Germany"
+ },
+ "currency": "EUR"
+ }
+ ],
+ "selected_country": "de",
+ "currency": "EUR",
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name",
+ "fr": "Nom complet",
+ "en": "Full name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_DE": "Geburtsdatum",
+ "de_CH": "Geburtsdatum",
+ "fr": "Date de naissance",
+ "en": "Birthdate"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "social_security_number",
+ "label": "Social security number",
+ "label_i18n": {
+ "de_DE": "Sozialversicherungsnummer",
+ "de_CH": "Sozialversicherungsnummer",
+ "fr": "Numéro de sécurité sociale",
+ "en": "Social security number"
+ },
+ "widget": "anastasis_gtk_ia_ssn"
+ }
+ ],
+ "authentication_providers": {
+ "question": [
+ {
+ "anastasis_04": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8089/",
+ "provider_name": "Anastasis 4",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_03": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8088/",
+ "provider_name": "Anastasis 3",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_02": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:1.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8087/",
+ "provider_name": "Anastasis 2",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_01": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8086/",
+ "provider_name": "Anastasis 1",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ }
+ ]
+ }
+ }
+
+
+**enter_user_attributes:**
+
+Arguments (example):
+::
+
+ {
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "social_security_number": "123456789",
+ "birth_year": "2000",
+ "birth_month": "01",
+ "birth_day": "01"
+ }
+ }
+
+Expected new state (backup process):
+::
+
+ {
+ "backup_state": "AUTHENTICATIONS_EDITING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ],
+ "selected_continent": "Europe",
+ "countries": [
+ {
+ "code": "ch",
+ "name": "Switzerland",
+ "continent": "Europe",
+ "name_i18n": {
+ "de_DE": "Schweiz",
+ "de_CH": "Schwiiz",
+ "fr": "Suisse",
+ "en": "Swiss"
+ },
+ "currency": "CHF"
+ },
+ {
+ "code": "de",
+ "name": "Germany",
+ "continent": "Europe",
+ "continent_i18n": {
+ "de": "Europa"
+ },
+ "name_i18n": {
+ "de_DE": "Deutschland",
+ "de_CH": "Deutschland",
+ "fr": "Allemagne",
+ "en": "Germany"
+ },
+ "currency": "EUR"
+ }
+ ],
+ "selected_country": "de",
+ "currency": "EUR",
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name",
+ "fr": "Nom complet",
+ "en": "Full name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_DE": "Geburtsdatum",
+ "de_CH": "Geburtsdatum",
+ "fr": "Date de naissance",
+ "en": "Birthdate"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "social_security_number",
+ "label": "Social security number",
+ "label_i18n": {
+ "de_DE": "Sozialversicherungsnummer",
+ "de_CH": "Sozialversicherungsnummer",
+ "fr": "Numéro de sécurité sociale",
+ "en": "Social security number"
+ },
+ "widget": "anastasis_gtk_ia_ssn"
+ }
+ ],
+ "authentication_providers": {
+ "question": [
+ {
+ "anastasis_04": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8089/",
+ "provider_name": "Anastasis 4",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_03": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8088/",
+ "provider_name": "Anastasis 3",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_02": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:1.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8087/",
+ "provider_name": "Anastasis 2",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_01": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8086/",
+ "provider_name": "Anastasis 1",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ }
+ ]
+ },
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "social_security_number": "123456789",
+ "birth_year": "2000",
+ "birth_month": "01",
+ "birth_day": "01"
+ }
+ }
+
+**next**, **back**, **pay:**
+
+No Arguments needed.
+
+
+Backup transitions
+""""""""""""""""""
+
+**add_authentication:**
+
+Arguments (example):
+::
+
+ {
+ "authentication_method":
+ {
+ "method": "question",
+ "data": {
+ "question": "Whats your name?",
+ "answer": "Hans"
+ }
+ }
+ }
+
+Expected new state:
+::
+
+ {
+ "backup_state": "AUTHENTICATIONS_EDITING",
+ "continents": [
+ "Europe",
+ "North_America"
+ ],
+ "selected_continent": "Europe",
+ "countries": [
+ {
+ "code": "ch",
+ "name": "Switzerland",
+ "continent": "Europe",
+ "name_i18n": {
+ "de_DE": "Schweiz",
+ "de_CH": "Schwiiz",
+ "fr": "Suisse",
+ "en": "Swiss"
+ },
+ "currency": "CHF"
+ },
+ {
+ "code": "de",
+ "name": "Germany",
+ "continent": "Europe",
+ "continent_i18n": {
+ "de": "Europa"
+ },
+ "name_i18n": {
+ "de_DE": "Deutschland",
+ "de_CH": "Deutschland",
+ "fr": "Allemagne",
+ "en": "Germany"
+ },
+ "currency": "EUR"
+ }
+ ],
+ "selected_country": "de",
+ "currency": "EUR",
+ "required_attributes": [
+ {
+ "type": "string",
+ "name": "full_name",
+ "label": "Full name",
+ "label_i18n": {
+ "de_DE": "Vollstaendiger Name",
+ "de_CH": "Vollstaendiger Name",
+ "fr": "Nom complet",
+ "en": "Full name"
+ },
+ "widget": "anastasis_gtk_ia_full_name"
+ },
+ {
+ "type": "date",
+ "name": "birthdate",
+ "label": "Birthdate",
+ "label_i18n": {
+ "de_DE": "Geburtsdatum",
+ "de_CH": "Geburtsdatum",
+ "fr": "Date de naissance",
+ "en": "Birthdate"
+ },
+ "widget": "anastasis_gtk_ia_birthdate"
+ },
+ {
+ "type": "string",
+ "name": "social_security_number",
+ "label": "Social security number",
+ "label_i18n": {
+ "de_DE": "Sozialversicherungsnummer",
+ "de_CH": "Sozialversicherungsnummer",
+ "fr": "Numéro de sécurité sociale",
+ "en": "Social security number"
+ },
+ "widget": "anastasis_gtk_ia_ssn"
+ }
+ ],
+ "authentication_providers": {
+ "question": [
+ {
+ "anastasis_04": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8089/",
+ "provider_name": "Anastasis 4",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_03": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8088/",
+ "provider_name": "Anastasis 3",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_02": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:1.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8087/",
+ "provider_name": "Anastasis 2",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ },
+ {
+ "anastasis_01": {
+ "method_cost": "EUR:0",
+ "annual_cost": "EUR:4.99",
+ "insurance": "EUR:1",
+ "provider_url": "localhost:8086/",
+ "provider_name": "Anastasis 1",
+ "provider_salt": "CXAPCKSH9D3MYJTS9536RHJHCW"
+ }
+ }
+ ]
+ },
+ "identity_attributes": {
+ "full_name": "Max Musterman",
+ "social_security_number": "123456789",
+ "birth_year": "2000",
+ "birth_month": "01",
+ "birth_day": "01"
+ },
+ "authentication_methods": [
+ {
+ "method": "question",
+ "data": {
+ "question": "Whats your name?",
+ "answer": "Hans"
+ }
+ }
+ ]
+ }
+
+**del_authentication:**
+
+Arguments (example):
+::
+
+ {
+ "auth_method_index": 3
+ }
+
+
+**add_policy:**
+
+Arguments (example):
+::
+
+ {
+ "policy": [
+ {
+ "auth_method_index": 1,
+ "provider": "anastasis_01"
+ },
+ {
+ "auth_method_index": 3,
+ "provider": "anastasis_02"
+ }
+ ]
+ }
+
+
+**del_policy:**
+
+Arguments (example):
+::
+
+ {
+ "policy_index": 3
+ }
+
+
+**enter_secret:**
+
+Arguments (example):
+::
+
+ {
+ "secret": "someverysecretsecret",
+ "type": "password"
+ }
+
+- type: *password* (secret is a password or passphrase)
+- type: *data* (secret must be a Crockford-Base32 encoded string of some data, e.g. a private key)
+
+
+Recovery transitions
+""""""""""""""""""""
+
+**select_challenge:**
+
+Arguments (example):
+::
+
+ {
+ "challenge_index": 1
+ }
+
+
+**solve_challenge:**
+
+Arguments (example):
+::
+
+ {
+ "challenge_index": 1,
+ "solution": "answer to secure question"
+ }
+
+
.. _anastasis-auth-methods:
----------------------
diff --git a/anastasis_reducer_backup.drawio b/anastasis_reducer_backup.drawio
index 9f4b2fa9..1248f7a4 100644
--- a/anastasis_reducer_backup.drawio
+++ b/anastasis_reducer_backup.drawio
@@ -1 +1 @@
-<mxfile host="Electron" modified="2020-10-20T12:02:13.988Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.7.9 Chrome/85.0.4183.121 Electron/10.1.3 Safari/537.36" etag="pjKoa-p90huBf9VOFbsK" version="13.7.9" type="device"><diagram id="PpkpfZO7TL7CUlFfbbxv" name="Seite-1">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</diagram></mxfile> \ No newline at end of file
+<mxfile host="Electron" modified="2020-11-06T14:41:36.477Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.7.9 Chrome/85.0.4183.121 Electron/10.1.5 Safari/537.36" etag="80drExZNCvYe0CJyHcSl" version="13.7.9" type="device"><diagram id="PpkpfZO7TL7CUlFfbbxv" name="Seite-1">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</diagram></mxfile> \ No newline at end of file
diff --git a/anastasis_reducer_backup.png b/anastasis_reducer_backup.png
index 2f2f537a..070bd7b0 100644
--- a/anastasis_reducer_backup.png
+++ b/anastasis_reducer_backup.png
Binary files differ
diff --git a/anastasis_reducer_recovery.drawio b/anastasis_reducer_recovery.drawio
index 54d14c28..ae7f8cad 100644
--- a/anastasis_reducer_recovery.drawio
+++ b/anastasis_reducer_recovery.drawio
@@ -1 +1 @@
-<mxfile host="Electron" modified="2020-10-20T12:07:27.387Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.7.9 Chrome/85.0.4183.121 Electron/10.1.3 Safari/537.36" etag="C5rf5ZT94Nhz4VqhFGuF" version="13.7.9" type="device"><diagram id="PpkpfZO7TL7CUlFfbbxv" name="Seite-1">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</diagram></mxfile> \ No newline at end of file
+<mxfile host="Electron" modified="2020-11-06T14:42:46.797Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.7.9 Chrome/85.0.4183.121 Electron/10.1.5 Safari/537.36" etag="WsVUn28slUkM80fea0rR" version="13.7.9" type="device"><diagram id="PpkpfZO7TL7CUlFfbbxv" name="Seite-1">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</diagram></mxfile> \ No newline at end of file
diff --git a/anastasis_reducer_recovery.png b/anastasis_reducer_recovery.png
index 7bb6241d..fb62c4da 100644
--- a/anastasis_reducer_recovery.png
+++ b/anastasis_reducer_recovery.png
Binary files differ
diff --git a/cf/captcha-payment.txt b/cf/captcha-payment.txt
index ecc065b5..4e24ffd9 100644
--- a/cf/captcha-payment.txt
+++ b/cf/captcha-payment.txt
@@ -98,7 +98,7 @@ Scalability:
double-spending detection. Also, a custom exchange could use
probabilistic data structures to improve efficiency.
Alternatively, multiple currencies could be used; for this, the
- HTTP header requiring auto-pay should simply specifiy the desired
+ HTTP header requiring auto-pay should simply specify the desired
currency, enabling further sharding of the load.
Benefits of integrated solution with Taler:
diff --git a/core/api-auditor.rst b/core/api-auditor.rst
index e76a72e2..74216bf7 100644
--- a/core/api-auditor.rst
+++ b/core/api-auditor.rst
@@ -40,7 +40,7 @@ know-your-customer (KYC) registration before issuing contracts.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The auditor responds with a `AuditorVersion`_ object. This request should
virtually always be successful.
@@ -85,7 +85,7 @@ know-your-customer (KYC) registration before issuing contracts.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The auditor responds with a :ts:type:`ExchangeList` object. This request should
virtually always be successful.
@@ -138,8 +138,9 @@ paid out first.
**Response:**
- :status 200: The auditor responds with a `DepositAudited` object.
- This request should virtually always be successful.
+ :http:statuscode:`200 Ok`:
+ The auditor responds with a `DepositAudited` object.
+ This request should virtually always be successful.
**Details:**
diff --git a/core/api-bank-access.rst b/core/api-bank-access.rst
index 5863ef78..456211b7 100644
--- a/core/api-bank-access.rst
+++ b/core/api-bank-access.rst
@@ -22,7 +22,7 @@ Taler Bank Access API
This chapter describes the API that the GNU Taler demonstrator bank offers to access accounts.
-This API differes from the "Bank Integration API" in that it provides advanced API access to accounts, as opposed
+This API differs from the "Bank Integration API" in that it provides advanced API access to accounts, as opposed
to enabling wallets to withdraw with a better user experience ("tight integration").
@@ -118,8 +118,8 @@ name and account password, at least in the GNU Taler demo bank implementation.
Abort a withdrawal operation. Has no effect on an already aborted withdrawal operation.
- :status 200 OK: The withdrawal operation has been aborted. The response is an empty JSON object.
- :status 409 Conflict: The reserve operation has been confirmed previously and can't be aborted.
+ :http:statuscode:`200 OK`: The withdrawal operation has been aborted. The response is an empty JSON object.
+ :http:statuscode:`409 Conflict`: The reserve operation has been confirmed previously and can't be aborted.
.. http:POST:: ${BANK_API_BASE_URL}/accounts/${account_name}/withdrawals/${withdrawal_id}/confirm
@@ -128,8 +128,8 @@ name and account password, at least in the GNU Taler demo bank implementation.
**Response**
- :status 200 OK: The withdrawal operation has been confirmed. The response is an empty JSON object.
- :status 409 Conflict: The reserve operation has been aborted previously and can't be confirmed.
+ :http:statuscode:`200 OK`: The withdrawal operation has been confirmed. The response is an empty JSON object.
+ :http:statuscode:`409 Conflict`: The reserve operation has been aborted previously and can't be confirmed.
@@ -157,4 +157,4 @@ Registration (Testing)
**Response**
- :status 200 OK: Registration was successful
+ :http:statuscode:`200 OK`: Registration was successful
diff --git a/core/api-bank-integration.rst b/core/api-bank-integration.rst
index 09e674f2..07299d6a 100644
--- a/core/api-bank-integration.rst
+++ b/core/api-bank-integration.rst
@@ -34,7 +34,7 @@ to tightly integrate with GNU Taler.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The exchange responds with a `BankVersion` object. This request should
virtually always be successful.
@@ -79,7 +79,7 @@ for the withdrawal operation (the ``wopid``) to interact with the withdrawal ope
**Response**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The withdrawal operation is known to the bank, and details are given
in the `BankWithdrawalOperationStatus` response body.
@@ -121,10 +121,10 @@ for the withdrawal operation (the ``wopid``) to interact with the withdrawal ope
**Response**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The bank has accepted the withdrawal operation parameters chosen by the wallet.
The response is a `BankWithdrawalOperationPostResponse`.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The bank does not know about a withdrawal operation with the specified ``wopid``.
**Details**
diff --git a/core/api-common.rst b/core/api-common.rst
index 7796bdec..5171a2c6 100644
--- a/core/api-common.rst
+++ b/core/api-common.rst
@@ -43,27 +43,28 @@ handle the error as if an internal error (500) had been returned.
**Response:**
:resheader Content-Type: application/json
- :status 200:
+
+ :http:statuscode:`200 Ok`:
The request was successful.
- :status 301 Moved Permanently:
+ :http:statuscode:`301 Moved permanently`:
The server responsible for the reserve
changed, the client MUST follow the link to the new location. If possible,
the client SHOULD remember the new URL for the reserve for future
requests. Only applicable if the request method is GET.
- :status 302 Found:
+ :http:statuscode:`302 Found`:
The server responsible for the reserve changed, the
client MUST follow the link to the new location, but MUST NOT retain the
new URL for future requests. Only applicable if the request method is GET.
- :status 307 Temporary Redirect:
+ :http:statuscode:`307 Temporary redirect`:
The server responsible for the reserve changed, the
client MUST follow the link to the new location, but MUST NOT retain the
new URL for future requests.
- :status 308 Permanent Redirect:
+ :http:statuscode:`308 Permanent redirect`:
The server responsible for the reserve
changed, the client MUST follow the link to the new location. If possible,
the client SHOULD remember the new URL for the reserve for future
requests.
- :status 500 Internal server error:
+ :http:statuscode:`500 Internal server error`:
This always indicates some serious internal operational error of the exchange,
such as a program bug, database problems, etc., and must not be used for
client-side problems. When facing an internal server error, clients should
@@ -76,7 +77,7 @@ handle the error as if an internal error (500) had been returned.
should naturally be able to address them in a timely fashion, especially
within 24h. When generating an internal server error, the exchange responds with
a JSON object containing the following fields:
- :status 400 Bad Request:
+ :http:statuscode:`400 Bad request`:
One of the arguments to the request is missing or malformed.
Unless specified otherwise, all error status codes (4xx and 5xx) have a message
@@ -114,7 +115,7 @@ handle the error as if an internal error (500) had been returned.
// Name of the object that was bogus (if applicable)
object?: string;
- // Name of the currency thant was problematic (if applicable)
+ // Name of the currency than was problematic (if applicable)
currency?: string;
// Expected type (if applicable).
@@ -335,6 +336,15 @@ Blinded coin
// Blinded coin's `public EdDSA key <eddsa-coin-pub>`, `base32` encoded
type CoinEnvelope = string;
+.. ts:def:: DenominationBlindingKeyP
+
+ // Secret for blinding/unblinding.
+ // An RSA blinding secret, which is basically
+ // a 256-bit nonce, converted to Crockford `Base32`.
+ type DenominationBlindingKeyP = string;
+
+
+
.. _signature:
Signatures
diff --git a/core/api-error.rst b/core/api-error.rst
index 88c43879..eb66d37d 100644
--- a/core/api-error.rst
+++ b/core/api-error.rst
@@ -258,7 +258,7 @@ The following list shows error codes defined in
/**
* The respective coin did not have sufficient residual value
* for the /deposit operation (i.e. due to double spending).
- * The "history" in the respose provides the transaction history
+ * The "history" in the response provides the transaction history
* of the coin proving this fact. This response is provided
* with HTTP status code MHD_HTTP_FORBIDDEN.
*/
@@ -690,7 +690,7 @@ The following list shows error codes defined in
* The exchange failed to recover information about the
* denomination key of the refunded coin (even though it
* recognizes the key). Hence it could not check the fee
- * strucutre.
+ * structure.
* This response is provided with HTTP status code
* MHD_HTTP_INTERNAL_SERVER_ERROR.
*/
@@ -982,7 +982,7 @@ The following list shows error codes defined in
/**
- * Integer overflow with sepcified timestamp argument detected.
+ * Integer overflow with specified timestamp argument detected.
* This response is provided
* with HTTP status code MHD_HTTP_BAD_REQUEST.
*/
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
index 15547ed5..2b4f3b2d 100644
--- a/core/api-exchange.rst
+++ b/core/api-exchange.rst
@@ -93,12 +93,15 @@ possibly by using HTTPS.
**Request:**
:query last_issue_date: optional argument specifying the maximum value of any of the "stamp_start" members of the denomination keys of a "/keys" response that is already known to the client. Allows the exchange to only return keys that have changed since that timestamp. The given value must be an unsigned 64-bit integer representing seconds after 1970. If the timestamp does not exactly match the "stamp_start" of one of the denomination keys, all keys are returned.
+ :query now: request that the exchange answer the request as if the current time were the value given in "now". The given value must be an unsigned 64-bit integer representing seconds after 1970. This option is used for testing, and in production is likely not supported.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The exchange responds with a `ExchangeKeysResponse` object. This request should
virtually always be successful.
+ :http:statuscode:`403 Forbidden`:
+ The client specified the "now" argument, but the exchange does not allow this option as per its configuration.
**Details:**
@@ -285,7 +288,8 @@ Obtaining wire-transfer information
**Response:**
- :status 200: The exchange responds with a `WireResponse` object. This request should virtually always be successful.
+ :http:statuscode:`200 Ok`:
+ The exchange responds with a `WireResponse` object. This request should virtually always be successful.
**Details:**
@@ -355,7 +359,7 @@ Withdrawal
This API is used by the wallet to obtain digital coins.
-When transfering money to the exchange such as via SEPA transfers, the exchange creates
+When transferring money to the exchange such as via SEPA transfers, the exchange creates
a *reserve*, which keeps the money from the customer. The customer must
specify an EdDSA reserve public key as part of the transfer, and can then
withdraw digital coins using the corresponding private key. All incoming and
@@ -387,9 +391,9 @@ exchange.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The exchange responds with a `ReserveStatus` object; the reserve was known to the exchange,
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The reserve key does not belong to a reserve known to the exchange.
**Details:**
@@ -523,13 +527,14 @@ exchange.
**Response:**
- :status 200 OK:
- The request was succesful, and the response is a `WithdrawResponse`. Note that repeating exactly the same request
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `WithdrawResponse`. Note that repeating exactly the same request
will again yield the same response, so if the network goes down during the
transaction or before the client can commit the coin signature to disk, the
coin is not lost.
- :status 401 Unauthorized: The signature is invalid.
- :status 404 Not Found:
+ :http:statuscode:`403 Forbidden`:
+ The signature is invalid.
+ :http:statuscode:`404 Not found`:
The denomination key or the reserve are not known to the exchange. If the
denomination key is unknown, this suggests a bug in the wallet as the
wallet should have used current denomination keys from ``/keys``. If the
@@ -538,7 +543,7 @@ exchange.
not yet have completed and might be known to the exchange in the near future.
In this case, the wallet should repeat the exact same request later again
using exactly the same blinded coin.
- :status 403 Forbidden:
+ :http:statuscode:`403 Forbidden`:
The balance of the reserve is not sufficient to withdraw a coin of the indicated denomination.
The response is `WithdrawError` object.
@@ -620,15 +625,15 @@ denomination.
**Response:**
- :status 200 Ok:
+ :http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that no double-spending took
place. The response will include a `DepositSuccess` object.
- :status 401 Unauthorized:
+ :http:statuscode:`401 Unauthorized`:
One of the signatures is invalid.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
Either the denomination key is not recognized (expired or invalid) or
the wire type is not recognized.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The deposit operation has either failed because the coin has insufficient
residual value, or because the same public key of the coin has been
previously used with a different denomination. Which case it is
@@ -857,14 +862,14 @@ the API during normal operation.
exchange. The exchange MUST return a 307 or 308 redirection to the correct
base URL if this is the case.
- :status 401 Unauthorized:
+ :http:statuscode:`200 OK`:
+ The request was successful. The response body is `MeltResponse` in this case.
+ :http:statuscode:`403 Forbidden`:
One of the signatures is invalid.
- :status 200 OK:
- The request was succesful. The response body is `MeltResponse` in this case.
- :status 404:
+ :http:statuscode:`404 Not found`:
the exchange does not recognize the denomination key as belonging to the exchange,
or it has expired
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The operation is not allowed as the coin has insufficient
residual value, or because the same public key of the coin has been
previously used with a different denomination. Which case it is
@@ -967,7 +972,7 @@ the API during normal operation.
.. http:post:: /refreshes/$RCH/reveal
- Reveal previously commited values to the exchange, except for the values
+ Reveal previously committed values to the exchange, except for the values
corresponding to the ``noreveal_index`` returned by the /coins/-melt step.
The $RCH is the hash over the refresh commitment from the /coins/-melt step
@@ -988,12 +993,12 @@ the API during normal operation.
not larger than the total remaining value of the coin before the melting
operations. Nevertheless, this is not really useful.
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The transfer private keys matched the commitment and the original request was well-formed.
The response body is a `RevealResponse`
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
There is a problem between the original commitment and the revealed private
- keys. The returned information is proof of the missmatch, and therefore
+ keys. The returned information is proof of the mismatch, and therefore
rather verbose, as it includes most of the original /refresh/melt request,
but of course expected to be primarily used for diagnostics.
The response body is a `RevealConflictResponse`.
@@ -1058,11 +1063,11 @@ the API during normal operation.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
All commitments were revealed successfully. The exchange returns an array,
typically consisting of only one element, in which each each element contains
information about a melting session that the coin was used in.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The exchange has no linkage data for the given public key, as the coin has not
yet been involved in a refresh operation.
@@ -1131,19 +1136,19 @@ in using this API.
**Response:**
- :status 200 OK:
- The request was succesful, and the response is a `RecoupConfirmation`.
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `RecoupConfirmation`.
Note that repeating exactly the same request
will again yield the same response, so if the network goes down during the
transaction or before the client can commit the coin signature to disk, the
coin is not lost.
- :status 401 Unauthorized:
+ :http:statuscode:`401 Unauthorized`:
The coin's signature is invalid.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The denomination key is not in the set of denomination
keys where emergency pay back is enabled, or the blinded
coin is not known to have been withdrawn.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The operation is not allowed as the coin has insufficient
residual value, or because the same public key of the coin has been
previously used with a different denomination. Which case it is
@@ -1242,10 +1247,10 @@ typically also view the balance.)
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The wire transfer is known to the exchange, details about it follow in the body.
The body of the response is a `TrackTransferResponse`.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The wire transfer identifier is unknown to the exchange.
.. ts:def:: TrackTransferResponse
@@ -1310,16 +1315,18 @@ typically also view the balance.)
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The deposit has been executed by the exchange and we have a wire transfer identifier.
The response body is a `TrackTransactionResponse` object.
- :status 202 Accepted:
+ :http:statuscode:`202 Accepted`:
The deposit request has been accepted for processing, but was not yet
executed. Hence the exchange does not yet have a wire transfer identifier. The
merchant should come back later and ask again.
The response body is a `TrackTransactionAcceptedResponse`.
- :status 401 Unauthorized: The signature is invalid.
- :status 404 Not Found: The deposit operation is unknown to the exchange
+ :http:statuscode:`401 Unauthorized`:
+ The signature is invalid.
+ :http:statuscode:`404 Not found`:
+ The deposit operation is unknown to the exchange
**Details:**
@@ -1371,20 +1378,20 @@ Refunds
**Response:**
- :status 200 Ok:
+ :http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that the coin can now be refreshed. The response will include a `RefundSuccess` object.
- :status 401 Unauthorized:
+ :http:statuscode:`401 Unauthorized`:
Merchant signature is invalid.
This response comes with a standard `ErrorDetail` response.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The refund operation failed as we could not find a matching deposit operation (coin, contract, transaction ID and merchant public key must all match).
This response comes with a standard `ErrorDetail` response.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The exchange has previously received a refund request for the same coin, merchant and contract, but specifying a different amount for the same refund transaction ID. The response will be a `RefundFailure` object.
- :status 410 Gone:
+ :http:statuscode:`410 Gone`:
It is too late for a refund by the exchange, the money was already sent to the merchant.
This response comes with a standard `ErrorDetail` response.
- :status 412 Precondition Failed:
+ :http:statuscode:`412 Precondition failed`:
The request transaction ID is identical to a previous refund request by the same
merchant for the same coin and contract, but the refund amount differs. (The
failed precondition is that the ``rtransaction_id`` is not unique.)
@@ -1431,21 +1438,21 @@ Refunds
exchange_pub: EddsaPublicKey;
}
- .. ts:def:: RefundFailure
+ .. ts:def:: RefundFailure
- interface RefundFailure {
+ interface RefundFailure {
- // Numeric error code unique to the condition, which can be either
- // related to the deposit value being insufficient for the requested
- // refund(s), or the requested refund conflicting due to refund
- // transaction number re-use (with different amounts).
- code: number;
+ // Numeric error code unique to the condition, which can be either
+ // related to the deposit value being insufficient for the requested
+ // refund(s), or the requested refund conflicting due to refund
+ // transaction number re-use (with different amounts).
+ code: number;
- // Human-readable description of the error message
- hint: string;
+ // Human-readable description of the error message
+ hint: string;
- // Information about the conflicting refund request(s).
- // This will not be the full history of the coin, but only
- // the relevant subset of the transactions.
- history: CoinSpendHistoryItem[];
- }
+ // Information about the conflicting refund request(s).
+ // This will not be the full history of the coin, but only
+ // the relevant subset of the transactions.
+ history: CoinSpendHistoryItem[];
+ }
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index 54e2492b..8282ce01 100644
--- a/core/api-merchant.rst
+++ b/core/api-merchant.rst
@@ -35,9 +35,12 @@ used.
.. contents:: Table of Contents
--------------------------
-Getting the configuration
--------------------------
+-----------------
+Configuration API
+-----------------
+
+The configuration API exposes basic information about a merchant backend,
+such as the implemented version of the protocol and the currency used.
.. http:get:: /config
@@ -45,7 +48,7 @@ Getting the configuration
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The exchange accepted all of the coins. The body is a `VersionResponse`.
.. ts:def:: VersionResponse
@@ -63,53 +66,725 @@ Getting the configuration
currency: string;
}
+
+----------
+Wallet API
+----------
---------------------------
-Dynamic Merchant Instances
---------------------------
+This section describes (public) endpoints that wallets must be able
+to interact with directly (without HTTP-based authentication). These
+endpoints are used to process payments (claiming an order, paying
+for the order, checking payment/refund status and aborting payments),
+process refunds (check refund status, obtain refund), and to pickup
+tips.
-.. _instances:
-.. http:get:: /private/instances
- This is used to return the list of all the merchant instances
+Claiming an order
+-----------------
+
+The first step of processing any Taler payment consists of the
+(authorized) wallet claiming the order for itself. In this process,
+the wallet provides a wallet-generated nonce that is added
+into the contract terms. This step prevents two different
+wallets from paying for the same contract, which would be bad
+especially if the merchant only has finite stocks.
+
+A claim token can be used to ensure that the wallet claiming an
+order is actually authorized to do so. This is useful in cases
+where order IDs are predictable and malicious actors may try to
+claim orders (say in a case where stocks are limited).
+
+
+.. http:post:: /orders/$ORDER_ID/claim
+
+ Wallet claims ownership (via nonce) over an order. By claiming
+ an order, the wallet obtains the full contract terms, and thereby
+ implicitly also the hash of the contract terms it needs for the
+ other ``public`` APIs to authenticate itself as the wallet that
+ is indeed eligible to inspect this particular order's status.
+
+ **Request:**
+
+ The request must be a `ClaimRequest`
+
+ .. ts:def:: ClaimRequest
+
+ interface ClaimRequest {
+ // Nonce to identify the wallet that claimed the order.
+ nonce: string;
+
+ // Token that authorizes the wallet to claim the order.
+ // *Optional* as the merchant may not have required it
+ // (``create_token`` set to ``false`` in `PostOrderRequest`).
+ token?: ClaimToken;
+ }
**Response:**
- :status 200 OK:
- The backend has successfully returned the list of instances stored. Returns
- a `InstancesResponse`.
+ :http:statuscode:`200 OK`:
+ The client has successfully claimed the order.
+ The response contains the :ref:`contract terms <contract-terms>`.
+ :http:statuscode:`404 Not found`:
+ The backend is unaware of the instance or order.
+ :http:statuscode:`409 Conflict`:
+ The someone else claimed the same order ID with different nonce before.
- .. ts:def:: InstancesResponse
+ .. ts:def:: ClaimResponse
- interface InstancesResponse {
- // List of instances that are present in the backend (see `Instance`)
- instances: Instance[];
+ interface ClaimResponse {
+ // Contract terms of the claimed order
+ contract_terms: ContractTerms;
+
+ // Signature by the merchant over the contract terms.
+ sig: EddsaSignature;
}
+
+Making the payment
+------------------
- The `Instance` object describes the instance registered with the backend.
- It does not include the full details, only those that usually concern the frontend.
- It has the following structure:
+.. http:post:: /orders/$ORDER_ID/pay
- .. ts:def:: Instance
+ Pay for an order by giving a deposit permission for coins. Typically used by
+ the customer's wallet. Note that this request does not include the
+ usual ``h_contract`` argument to authenticate the wallet, as the hash of
+ the contract is implied by the signatures of the coins. Furthermore, this
+ API doesn't really return useful information about the order.
- interface Instance {
- // Merchant name corresponding to this instance.
- name: string;
+ **Request:**
- // Merchant instance this response is about ($INSTANCE)
- id: string;
+ The request must be a `pay request <PayRequest>`.
- // Public key of the merchant/instance, in Crockford Base32 encoding.
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The exchange accepted all of the coins.
+ The body is a `payment response <PaymentResponse>`.
+ The ``frontend`` should now fulfill the contract.
+ Note that it is possible that refunds have been granted.
+ :http:statuscode:`400 Bad request`:
+ Either the client request is malformed or some specific processing error
+ happened that may be the fault of the client as detailed in the JSON body
+ of the response.
+ :http:statuscode:`402 Payment required`:
+ There used to be a sufficient payment, but due to refunds the amount effectively
+ paid is no longer sufficient. (If the amount is generally insufficient, we
+ return "406 Not Acceptable", only if this is because of refunds we return 402.)
+ :http:statuscode:`403 Forbidden`:
+ One of the coin signatures was not valid.
+ :http:statuscode:`404 Not found`:
+ The merchant backend could not find the order or the instance and thus cannot process the payment.
+ :http:statuscode:`406 Not acceptable`:
+ The payment is insufficient (sum is below the required total amount).
+ :http:statuscode:`408 Request timeout`:
+ The backend took too long to process the request. Likely the merchant's connection
+ to the exchange timed out. Try again.
+ :http:statuscode:`409 Conflict`:
+ The exchange rejected the payment because a coin was already spent before.
+ The response will include the ``coin_pub`` for which the payment failed,
+ in addition to the response from the exchange to the ``/deposit`` request.
+ :http:statuscode:`410 Gone`:
+ The offer has expired and is no longer available.
+ :http:statuscode:`412 Precondition failed`:
+ The given exchange is not acceptable for this merchant, as it is not in the
+ list of accepted exchanges and not audited by an approved auditor.
+ :http:statuscode:`424 Failed dependency`:
+ The merchant's interaction with the exchange failed in some way.
+ The client might want to try later again.
+ This includes failures like the denomination key of a coin not being
+ known to the exchange as far as the merchant can tell.
+
+ The backend will return verbatim the error codes received from the exchange's
+ :ref:`deposit <deposit>` API. If the wallet made a mistake, like by
+ double-spending for example, the frontend should pass the reply verbatim to
+ the browser/wallet. If the payment was successful, the frontend MAY use
+ this to trigger some business logic.
+
+ .. ts:def:: PaymentResponse
+
+ interface PaymentResponse {
+ // Signature on ``TALER_PaymentResponsePS`` with the public
+ // key of the merchant instance.
+ sig: EddsaSignature;
+
+ }
+
+ .. ts:def:: PayRequest
+
+ interface PayRequest {
+ // The coins used to make the payment.
+ coins: CoinPaySig[];
+
+ // The session for which the payment is made (or replayed).
+ // Only set for session-based payments.
+ session_id?: string;
+
+ }
+
+ .. ts:def:: CoinPaySig
+
+ export interface CoinPaySig {
+ // Signature by the coin.
+ coin_sig: EddsaSignature;
+
+ // Public key of the coin being spend.
+ coin_pub: EddsaPublicKey;
+
+ // Signature made by the denomination public key.
+ ub_sig: RsaSignature;
+
+ // The hash of the denomination public key associated with this coin.
+ h_denom: HashCode;
+
+ // The amount that is subtracted from this coin with this payment.
+ contribution: Amount;
+
+ // URL of the exchange this coin was withdrawn from.
+ exchange_url: string;
+ }
+
+Querying payment status
+-----------------------
+
+.. http:get:: /orders/$ORDER_ID
+
+ Query the payment status of an order. This endpoint is for the wallet.
+ When the wallet goes to this URL and it is unpaid,
+ they will be prompted for payment.
+ This endpoint typically also supports requests with the "Accept" header
+ requesting "text/html". In this case, an HTML response suitable for
+ triggering the interaction with the wallet is returned, with ``timeout_ms``
+ ignored (treated as zero). If the backend installation does not include the
+ required HTML templates, a 406 status code is returned.
+
+ In the case that the request was made with a claim token (even the wrong one)
+ and the order was claimed and paid, the server will redirect the client to
+ the fulfillment URL. This redirection will happen with a 302 status code
+ if the "Accept" header specified "text/html", and with a 202 status code
+ otherwise.
+
+ **Request:**
+
+ :query h_contract=HASH: hash of the order's contract terms (this is used to authenticate the wallet/customer in case $ORDER_ID is guessable). Required once an order was claimed.
+ :query token=TOKEN: *Optional*. Authorizes the request via the claim token that was returned in the `PostOrderResponse`. Used with unclaimed orders only. Whether token authorization is required is determined by the merchant when the frontend creates the order.
+ :query session_id=STRING: *Optional*. Session ID that the payment must be bound to. If not specified, the payment is not session-bound.
+ :query timeout_ms=NUMBER: *Optional.* If specified, the merchant backend will
+ wait up to ``timeout_ms`` milliseconds for completion of the payment before
+ sending the HTTP response. A client must never rely on this behavior, as the
+ merchant backend may return a response immediately.
+ :query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund above the given AMOUNT. Only useful in combination with timeout.
+ :query await_refund_obtained=BOOLEAN: *Optional*. If set to "yes", poll for the order's pending refunds to be picked up.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The response is a `StatusPaidResponse`.
+ :http:statuscode:`202 Accepted`:
+ The response is a `StatusGotoResponse`. Only returned if the content type requested was not HTML.
+ :http:statuscode:`302 Found`:
+ The client should go to the indicated location. Only returned if the content type requested was HTML.
+ :http:statuscode:`402 Payment required`:
+ The response is a `StatusUnpaidResponse`.
+ :http:statuscode:`403 Forbidden`:
+ The ``h_contract`` (or the ``token`` for unclaimed orders) does not match the order
+ and we have no fulfillment URL in the contract.
+ :http:statuscode:`410 Gone`:
+ The response is a `StatusGoneResponse`.
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the order.
+ :http:statuscode:`406 Not acceptable`:
+ The merchant backend could not load the template required to generate a reply in the desired format. (Likely HTML templates were not properly installed.)
+
+ .. ts:def:: StatusPaidResponse
+
+ interface StatusPaid {
+ // Was the payment refunded (even partially, via refund or abort)?
+ refunded: boolean;
+
+ // Is any amount of the refund still waiting to be picked up (even partially)
+ refund_pending: boolean;
+
+ // Amount that was refunded in total.
+ refund_amount: Amount;
+ }
+
+ .. ts:def:: StatusGotoResponse
+
+ interface StatusGotoResponse {
+ // The client should go to the fulfillment URL, it may be ready or
+ // might have some other interesting status.
+ fulfillment_url: string;
+ }
+
+ .. ts:def:: StatusUnpaidResponse
+
+ interface StatusUnpaidResponse {
+ // URI that the wallet must process to complete the payment.
+ taler_pay_uri: string;
+
+ // Status URL, can be used as a redirect target for the browser
+ // to show the order QR code / trigger the wallet.
+ fulfillment_url?: string;
+
+ // Alternative order ID which was paid for already in the same session.
+ // Only given if the same product was purchased before in the same session.
+ already_paid_order_id?: string;
+ }
+
+ .. ts:def:: StatusGoneResponse
+
+ // The client tried to access the order via the claim
+ // token (and not a valid h_contract), but the order can't be claimed
+ // anymore, as it is already paid.
+ interface StatusGoneResponse {
+ // Fulfillment URL for the order.
+ fulfillment_url: string;
+ }
+
+
+Demonstrating payment
+---------------------
+
+In case a wallet has already paid for an order, this is a fast way of proving
+to the merchant that the order was already paid. The alternative would be to
+replay the original payment, but simply providing the merchant's signature
+saves bandwidth and computation time.
+
+Demonstrating payment is useful in case a digital good was made available
+only to clients with a particular session ID: if that session ID expired or
+if the user is using a different client, demonstrating payment will allow
+the user to regain access to the digital good without having to pay for it
+again.
+
+.. http:post:: /orders/$ORDER_ID/paid
+
+ Prove that the client previously paid for an order by providing
+ the merchant's signature from the `payment response <PaymentResponse>`.
+ Typically used by the customer's wallet if it receives a request for
+ payment for an order that it already paid. This is more compact then
+ re-transmitting the full payment details.
+ Note that this request does include the
+ usual ``h_contract`` argument to authenticate the wallet and
+ to allow the merchant to verify the signature before checking
+ with its own database.
+
+ **Request:**
+
+ The request must be a `paid request <PaidRequest>`.
+
+ **Response:**
+
+ :http:statuscode:`204 No content`:
+ The merchant accepted the signature.
+ The ``frontend`` should now fulfill the contract.
+ Note that it is possible that refunds have been granted.
+ :http:statuscode:`400 Bad request`:
+ Either the client request is malformed or some specific processing error
+ happened that may be the fault of the client as detailed in the JSON body
+ of the response.
+ :http:statuscode:`403 Forbidden`:
+ The signature was not valid.
+ :http:statuscode:`404 Not found`:
+ The merchant backend could not find the order or the instance
+ and thus cannot process the request.
+ :http:statuscode:`409 Conflict`:
+ The provided contract hash does not match this order.
+
+ .. ts:def:: PaidRequest
+
+ interface PaidRequest {
+ // Signature on ``TALER_PaymentResponsePS`` with the public
+ // key of the merchant instance.
+ sig: EddsaSignature;
+
+ // hash of the order's contract terms (this is used to authenticate the
+ // wallet/customer and to enable signature verification without
+ // database access).
+ h_contract: HashCode;
+
+ // Session id for which the payment is proven.
+ session_id: string;
+ }
+
+
+Aborting incomplete payments
+----------------------------
+
+In rare cases (such as a wallet restoring from an outdated backup) it is possible
+that a wallet fails to complete a payment because it runs out of e-cash in the
+middle of the process. The abort API allows the wallet to abort the payment for
+such an incomplete payment and to regain control over the coins that were spent
+so far. Aborts are not permitted for payments that completed. In contrast to
+refunds, aborts do not require approval by the merchant because aborts always
+are for incomplete payments for an order and never for established contracts.
+
+
+.. _order-abort:
+.. http:post:: /orders/$ORDER_ID/abort
+
+ Abort paying for an order and obtain a refund for coins that
+ were already deposited as part of a failed payment.
+
+ **Request:**
+
+ The request must be an `abort request <AbortRequest>`. We force the wallet
+ to specify the affected coins as it may only request for a subset of the coins
+ (i.e. because the wallet knows that some were double-spent causing the failure).
+ Also we need to know the coins because there may be two wallets "competing" over
+ the same order and one wants to abort while the other still proceeds with the
+ payment. Here we need to again know which subset of the deposits to abort.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The merchant accepted the request, and passed it on to the exchange. The body is a
+ a `merchant refund response <MerchantRefundResponse>`. Note that the exchange
+ MAY still have encountered errors in processing. Those will then be part of
+ the body. Wallets MUST carefully consider errors for each of the coins as
+ returned by the exchange.
+ :http:statuscode:`400 Bad request`:
+ Either the client request is malformed or some specific processing error
+ happened that may be the fault of the client as detailed in the JSON body
+ of the response.
+ :http:statuscode:`403 Forbidden`:
+ The ``h_contract`` does not match the $ORDER_ID.
+ :http:statuscode:`404 Not found`:
+ The merchant backend could not find the order or the instance
+ and thus cannot process the abort request.
+ :http:statuscode:`408 Request timeout`:
+ The merchant backend took too long getting a response from the exchange.
+ The wallet SHOULD retry soon.
+ :http:statuscode:`412 Precondition failed`:
+ Aborting the payment is not allowed, as the original payment did succeed.
+ It is possible that a different wallet succeeded with the payment. This
+ wallet should thus try to refresh all of the coins involved in the payment.
+ :http:statuscode:`424 Failed dependency`:
+ The merchant's interaction with the exchange failed in some way.
+ The error from the exchange is included.
+
+ The backend will return an `abort response <AbortResponse>`, which includes
+ verbatim the error codes received from the exchange's
+ :ref:`refund <exchange_refund>` API. The frontend should pass the replies verbatim to
+ the browser/wallet.
+
+ .. ts:def:: AbortRequest
+
+ interface AbortRequest {
+
+ // hash of the order's contract terms (this is used to authenticate the
+ // wallet/customer in case $ORDER_ID is guessable).
+ h_contract: HashCode;
+
+ // List of coins the wallet would like to see refunds for.
+ // (Should be limited to the coins for which the original
+ // payment succeeded, as far as the wallet knows.)
+ coins: AbortingCoin[];
+ }
+
+ .. ts:def:: AbortingCoin
+
+ interface AbortingCoin {
+ // Public key of a coin for which the wallet is requesting an abort-related refund.
+ coin_pub: EddsaPublicKey;
+
+ // The amount to be refunded (matches the original contribution)
+ contribution: Amount;
+
+ // URL of the exchange this coin was withdrawn from.
+ exchange_url: string;
+ }
+
+
+ .. ts:def:: AbortResponse
+
+ interface AbortResponse {
+
+ // List of refund responses about the coins that the wallet
+ // requested an abort for. In the same order as the 'coins'
+ // from the original request.
+ // The rtransaction_id is implied to be 0.
+ refunds: MerchantAbortPayRefundStatus[];
+ }
+
+ .. ts:def:: MerchantAbortPayRefundStatus
+
+ type MerchantAbortPayRefundStatus =
+ | MerchantAbortPayRefundSuccessStatus
+ | MerchantAbortPayRefundFailureStatus;
+
+ .. ts:def:: MerchantAbortPayRefundFailureStatus
+
+ // Details about why a refund failed.
+ interface MerchantAbortPayRefundFailureStatus {
+ // Used as tag for the sum type RefundStatus sum type.
+ type: "failure"
+
+ // HTTP status of the exchange request, must NOT be 200.
+ exchange_status: Integer;
+
+ // Taler error code from the exchange reply, if available.
+ exchange_code?: Integer;
+
+ // If available, HTTP reply from the exchange.
+ exchange_reply?: Object;
+ }
+
+ .. ts:def:: MerchantAbortPayRefundSuccessStatus
+
+ // Additional details needed to verify the refund confirmation signature
+ // (``h_contract_terms`` and ``merchant_pub``) are already known
+ // to the wallet and thus not included.
+ interface MerchantAbortPayRefundSuccessStatus {
+ // Used as tag for the sum type MerchantCoinRefundStatus sum type.
+ type: "success"
+
+ // HTTP status of the exchange request, 200 (integer) required for refund confirmations.
+ exchange_status: 200;
+
+ // the EdDSA :ref:`signature` (binary-only) with purpose
+ // `TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND` using a current signing key of the
+ // exchange affirming the successful refund
+ exchange_sig: EddsaSignature;
+
+ // public EdDSA key of the exchange that was used to generate the signature.
+ // Should match one of the exchange's signing keys from /keys. It is given
+ // explicitly as the client might otherwise be confused by clock skew as to
+ // which signing key was used.
+ exchange_pub: EddsaPublicKey;
+ }
+
+
+Obtaining refunds
+-----------------
+
+Refunds allow merchants to fully or partially restitute e-cash to a wallet,
+for example because the merchant determined that it could not actually fulfill
+the contract. Refunds must be approved by the merchant's business logic.
+
+
+.. http:post:: /orders/$ORDER_ID/refund
+
+ Obtain refunds for an order. After talking to the exchange, the refunds will
+ no longer be pending if processed successfully.
+
+ **Request:**
+
+ The request body is a `WalletRefundRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The response is a `WalletRefundResponse`.
+ :http:statuscode:`204 No content`:
+ There are no refunds for the order.
+ :http:statuscode:`403 Forbidden`:
+ The ``h_contract`` does not match the order.
+ :http:statuscode:`404 Not found`:
+ The merchant backend is unaware of the order.
+
+ .. ts:def:: WalletRefundRequest
+
+ interface WalletRefundRequest {
+ // hash of the order's contract terms (this is used to authenticate the
+ // wallet/customer).
+ h_contract: HashCode;
+ }
+
+ .. ts:def:: WalletRefundResponse
+
+ interface WalletRefundResponse {
+ // Amount that was refunded in total.
+ refund_amount: Amount;
+
+ // Successful refunds for this payment, empty array for none.
+ refunds: MerchantCoinRefundStatus[];
+
+ // Public key of the merchant.
merchant_pub: EddsaPublicKey;
- // List of the payment targets supported by this instance. Clients can
- // specify the desired payment target in /order requests. Note that
- // front-ends do not have to support wallets selecting payment targets.
- payment_targets: string[];
+ }
- }
+ .. ts:def:: MerchantCoinRefundStatus
+
+ type MerchantCoinRefundStatus =
+ | MerchantCoinRefundSuccessStatus
+ | MerchantCoinRefundFailureStatus;
+
+ .. ts:def:: MerchantCoinRefundFailureStatus
+
+ // Details about why a refund failed.
+ interface MerchantCoinRefundFailureStatus {
+ // Used as tag for the sum type RefundStatus sum type.
+ type: "failure";
+
+ // HTTP status of the exchange request, must NOT be 200.
+ exchange_status: Integer;
+
+ // Taler error code from the exchange reply, if available.
+ exchange_code?: Integer;
+
+ // If available, HTTP reply from the exchange.
+ exchange_reply?: Object;
+
+ // Refund transaction ID.
+ rtransaction_id: Integer;
+
+ // public key of a coin that was refunded
+ coin_pub: EddsaPublicKey;
+
+ // Amount that was refunded, including refund fee charged by the exchange
+ // to the customer.
+ refund_amount: Amount;
+ }
+
+ .. ts:def:: MerchantCoinRefundSuccessStatus
+
+ // Additional details needed to verify the refund confirmation signature
+ // (``h_contract_terms`` and ``merchant_pub``) are already known
+ // to the wallet and thus not included.
+ interface MerchantCoinRefundSuccessStatus {
+ // Used as tag for the sum type MerchantCoinRefundStatus sum type.
+ type: "success";
+
+ // HTTP status of the exchange request, 200 (integer) required for refund confirmations.
+ exchange_status: 200;
+
+ // the EdDSA :ref:`signature` (binary-only) with purpose
+ // `TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND` using a current signing key of the
+ // exchange affirming the successful refund
+ exchange_sig: EddsaSignature;
+
+ // public EdDSA key of the exchange that was used to generate the signature.
+ // Should match one of the exchange's signing keys from /keys. It is given
+ // explicitly as the client might otherwise be confused by clock skew as to
+ // which signing key was used.
+ exchange_pub: EddsaPublicKey;
+
+ // Refund transaction ID.
+ rtransaction_id: Integer;
+
+ // public key of a coin that was refunded
+ coin_pub: EddsaPublicKey;
+
+ // Amount that was refunded, including refund fee charged by the exchange
+ // to the customer.
+ refund_amount: Amount;
+ }
+
+
+Picking up tips
+---------------
+
+Tips are a way for wallets to obtain e-cash from
+a website.
+
+.. http:get:: /tips/$TIP_ID
+
+ Handle request from wallet to provide details about a tip.
+
+ This endpoint typically also supports requests with the "Accept" header
+ requesting "text/html". In this case, an HTML response suitable for
+ triggering the interaction with the wallet is returned. If the backend
+ installation does not include the required HTML templates, a 406 status
+ code is returned.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ A tip is being returned. The backend responds with a `TipInformation`.
+ :http:statuscode:`404 Not found`:
+ The tip identifier is unknown.
+ :http:statuscode:`406 Not acceptable`:
+ The merchant backend could not load the template required to generate a reply in the desired format. (Likely HTML templates were not properly installed.)
+ :http:statuscode:`410 Gone`:
+ A tip has been fully claimed. The JSON reply still contains the `TipInformation`.
+
+ .. ts:def:: TipInformation
+
+ interface TipInformation {
+
+ // Exchange from which the tip will be withdrawn. Needed by the
+ // wallet to determine denominations, fees, etc.
+ exchange_url: string;
+
+ // (remaining) amount of the tip (including fees).
+ tip_amount: Amount;
+
+ // Timestamp indicating when the tip is set to expire (may be in the past).
+ // Note that tips that have expired MAY also result in a 404 response.
+ expiration: Timestamp;
+ }
+
+
+.. http:post:: /tips/$TIP_ID/pickup
+
+ Handle request from wallet to pick up a tip.
+
+ **Request:**
+
+ The request body is a `TipPickupRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ A tip is being returned. The backend responds with a `TipResponse`
+ :http:statuscode:`401 Unauthorized`:
+ The tip amount requested exceeds the tip.
+ :http:statuscode:`404 Not found`:
+ The tip identifier is unknown.
+ :http:statuscode:`409 Conflict`:
+ Some of the denomination key hashes of the request do not match those currently available from the exchange (hence there is a conflict between what the wallet requests and what the merchant believes the exchange can provide).
+ :http:statuscode:`410 Gone`:
+ The tip has expired.
+
+ .. ts:def:: TipPickupRequest
+
+ interface TipPickupRequest {
+ // List of planches the wallet wants to use for the tip
+ planchets: PlanchetDetail[];
+ }
+
+ .. ts:def:: PlanchetDetail
+
+ interface PlanchetDetail {
+ // Hash of the denomination's public key (hashed to reduce
+ // bandwidth consumption)
+ denom_pub_hash: HashCode;
+
+ // coin's blinded public key
+ coin_ev: CoinEnvelope;
+ }
+
+ .. ts:def:: TipResponse
+
+ interface TipResponse {
+
+ // Blind RSA signatures over the planchets.
+ // The order of the signatures matches the planchets list.
+ blind_sigs: BlindSignature[];
+ }
+
+ .. ts:def:: BlindSignature
+
+ interface BlindSignature {
+
+ // The (blind) RSA signature. Still needs to be unblinded.
+ blind_sig: BlindedRsaSignature;
+ }
+
+
+-------------------
+Instance management
+-------------------
+
+Instances allow one merchant backend to be shared by multiple merchants.
+Every backend must have at least one instance, typcially the "default"
+instance setup before it can be used to manage inventory or process payments.
+
+
+Setting up instances
+--------------------
.. http:post:: /private/instances
@@ -121,9 +796,9 @@ Dynamic Merchant Instances
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully created the instance.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
This instance already exists, but with other configuration options.
Use "PATCH" to update an instance configuration.
@@ -156,7 +831,7 @@ Dynamic Merchant Instances
default_max_wire_fee: Amount;
// Default factor for wire fee amortization calculations.
- // Can be overriden by the frontend on a per-order basis.
+ // Can be overridden by the frontend on a per-order basis.
default_wire_fee_amortization: Integer;
// Maximum deposit fee (sum over all coins) this instance is willing to pay.
@@ -188,9 +863,9 @@ Dynamic Merchant Instances
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully created the instance.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
This instance is unknown and thus cannot be reconfigured.
.. ts:def:: InstanceReconfigurationMessage
@@ -216,7 +891,7 @@ Dynamic Merchant Instances
default_max_wire_fee: Amount;
// Default factor for wire fee amortization calculations.
- // Can be overriden by the frontend on a per-order basis.
+ // Can be overridden by the frontend on a per-order basis.
default_wire_fee_amortization: Integer;
// Maximum deposit fee (sum over all coins) this instance is willing to pay.
@@ -236,13 +911,58 @@ Dynamic Merchant Instances
}
+Inspecting instances
+--------------------
+
+.. _instances:
+.. http:get:: /private/instances
+
+ This is used to return the list of all the merchant instances
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the list of instances stored. Returns
+ a `InstancesResponse`.
+
+ .. ts:def:: InstancesResponse
+
+ interface InstancesResponse {
+ // List of instances that are present in the backend (see `Instance`)
+ instances: Instance[];
+ }
+
+ The `Instance` object describes the instance registered with the backend.
+ It does not include the full details, only those that usually concern the frontend.
+ It has the following structure:
+
+ .. ts:def:: Instance
+
+ interface Instance {
+ // Merchant name corresponding to this instance.
+ name: string;
+
+ // Merchant instance this response is about ($INSTANCE)
+ id: string;
+
+ // Public key of the merchant/instance, in Crockford Base32 encoding.
+ merchant_pub: EddsaPublicKey;
+
+ // List of the payment targets supported by this instance. Clients can
+ // specify the desired payment target in /order requests. Note that
+ // front-ends do not have to support wallets selecting payment targets.
+ payment_targets: string[];
+
+ }
+
+
.. http:get:: /private/instances/$INSTANCE
This is used to query a specific merchant instance.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The backend has successfully returned the list of instances stored. Returns
a `QueryInstancesResponse`.
@@ -271,7 +991,7 @@ Dynamic Merchant Instances
default_max_wire_fee: Amount;
// Default factor for wire fee amortization calculations.
- // Can be overriden by the frontend on a per-order basis.
+ // Can be overridden by the frontend on a per-order basis.
default_wire_fee_amortization: Integer;
// Maximum deposit fee (sum over all coins) this instance is willing to pay.
@@ -308,7 +1028,9 @@ Dynamic Merchant Instances
active: boolean;
}
-
+
+Deleting instances
+------------------
.. http:delete:: /private/instances/$INSTANCE
@@ -316,7 +1038,7 @@ Dynamic Merchant Instances
or purge merchant instance in the backend. Purging will
delete all offers and payments associated with the instance,
while disabling (the default) only deletes the private key
- and makes the instance unusuable for new orders or payments.
+ and makes the instance unusable for new orders or payments.
**Request:**
@@ -325,11 +1047,11 @@ Dynamic Merchant Instances
**Response**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully removed the instance. The body is empty.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The instance is unknown to the backend.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The instance cannot be deleted because it has pending offers, or
the instance cannot be purged because it has successfully processed
payments that have not passed the TAX_RECORD_EXPIRATION time.
@@ -349,47 +1071,31 @@ Taler merchant backend to process payments *without* using its inventory
management.
-.. http:get:: /private/products
-
- This is used to return the list of all items in the inventory.
-
- **Response:**
-
- :status 200 OK:
- The backend has successfully returned the inventory. Returns
- a `InventorySummaryResponse`.
-
- .. ts:def:: InventorySummaryResponse
-
- interface InventorySummaryResponse {
- // List of products that are present in the inventory
- products: InventoryEntry[];
- }
-
- The `InventoryEntry` object describes an item in the inventory. It has the following structure:
+Adding products to the inventory
+--------------------------------
- .. ts:def:: InventoryEntry
+.. http:post:: /private/products
- interface InventoryEntry {
- // Product identifier, as found in the product.
- product_id: string;
+ This is used to add a product to the inventory.
- }
+ **Request:**
+ The request must be a `ProductAddDetail`.
-.. http:get:: /private/products/$PRODUCT_ID
+ **Response:**
- This is used to obtain detailed information about a product in the inventory.
+ :http:statuscode:`204 No content`:
+ The backend has successfully expanded the inventory.
+ :http:statuscode:`409 Conflict`:
+ The backend already knows a product with this product ID, but with different details.
- **Response:**
- :status 200 OK:
- The backend has successfully returned the inventory. Returns
- a `ProductDetail`.
+ .. ts:def:: ProductAddDetail
- .. ts:def:: ProductDetail
+ interface ProductAddDetail {
- interface ProductDetail {
+ // product ID to use.
+ product_id: string;
// Human-readable product description.
description: string;
@@ -419,12 +1125,6 @@ management.
// A value of -1 indicates "infinite" (i.e. for "electronic" books).
total_stock: Integer;
- // Number of units of the product that have already been sold.
- total_sold: Integer;
-
- // Number of units of the product that were lost (spoiled, stolen, etc.)
- total_lost: Integer;
-
// Identifies where the product is in stock.
address: Location;
@@ -434,28 +1134,33 @@ management.
}
-.. http:post:: /private/products
- This is used to add a product to the inventory.
+.. http:patch:: /private/products/$PRODUCT_ID
+
+ This is used to update product details in the inventory. Note that the
+ ``total_stock`` and ``total_lost`` numbers MUST be greater or equal than
+ previous values (this design ensures idempotency). In case stocks were lost
+ but not sold, increment the ``total_lost`` number. All fields in the
+ request are optional, those that are not given are simply preserved (not
+ modified). Note that the ``description_i18n`` and ``taxes`` can only be
+ modified in bulk: if it is given, all translations must be provided, not
+ only those that changed. "never" should be used for the ``next_restock``
+ timestamp to indicate no intention/possibility of restocking, while a time
+ of zero is used to indicate "unknown".
**Request:**
- The request must be a `ProductAddDetail`.
+ The request must be a `ProductPatchDetail`.
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully expanded the inventory.
- :status 409 Conflict:
- The backend already knows a product with this product ID, but with different details.
-
- .. ts:def:: ProductAddDetail
- interface ProductAddDetail {
+ .. ts:def:: ProductPatchDetail
- // product ID to use.
- product_id: string;
+ interface ProductPatchDetail {
// Human-readable product description.
description: string;
@@ -485,6 +1190,9 @@ management.
// A value of -1 indicates "infinite" (i.e. for "electronic" books).
total_stock: Integer;
+ // Number of units of the product that were lost (spoiled, stolen, etc.)
+ total_lost: Integer;
+
// Identifies where the product is in stock.
address: Location;
@@ -493,34 +1201,50 @@ management.
}
+Inspecting inventory
+--------------------
+.. http:get:: /private/products
-.. http:patch:: /private/products/$PRODUCT_ID
+ This is used to return the list of all items in the inventory.
- This is used to update product details in the inventory. Note that the
- ``total_stock`` and ``total_lost`` numbers MUST be greater or equal than
- previous values (this design ensures idempotency). In case stocks were lost
- but not sold, increment the ``total_lost`` number. All fields in the
- request are optional, those that are not given are simply preserved (not
- modified). Note that the ``description_i18n`` and ``taxes`` can only be
- modified in bulk: if it is given, all translations must be provided, not
- only those that changed. "never" should be used for the ``next_restock``
- timestamp to indicate no intention/possibility of restocking, while a time
- of zero is used to indicate "unknown".
+ **Response:**
- **Request:**
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the inventory. Returns
+ a `InventorySummaryResponse`.
- The request must be a `ProductPatchDetail`.
+ .. ts:def:: InventorySummaryResponse
- **Response:**
+ interface InventorySummaryResponse {
+ // List of products that are present in the inventory
+ products: InventoryEntry[];
+ }
- :status 204 No content:
- The backend has successfully expanded the inventory.
+ The `InventoryEntry` object describes an item in the inventory. It has the following structure:
+ .. ts:def:: InventoryEntry
- .. ts:def:: ProductPatchDetail
+ interface InventoryEntry {
+ // Product identifier, as found in the product.
+ product_id: string;
- interface ProductPatchDetail {
+ }
+
+
+.. http:get:: /private/products/$PRODUCT_ID
+
+ This is used to obtain detailed information about a product in the inventory.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the inventory. Returns
+ a `ProductDetail`.
+
+ .. ts:def:: ProductDetail
+
+ interface ProductDetail {
// Human-readable product description.
description: string;
@@ -550,6 +1274,9 @@ management.
// A value of -1 indicates "infinite" (i.e. for "electronic" books).
total_stock: Integer;
+ // Number of units of the product that have already been sold.
+ total_sold: Integer;
+
// Number of units of the product that were lost (spoiled, stolen, etc.)
total_lost: Integer;
@@ -562,6 +1289,8 @@ management.
}
+Reserving inventory
+-------------------
.. http:post:: /private/products/$PRODUCT_ID/lock
@@ -587,11 +1316,11 @@ management.
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully locked (or unlocked) the requested ``quantity``.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The backend has does not know this product.
- :status 410 Gone:
+ :http:statuscode:`410 Gone`:
The backend does not have enough of product in stock.
.. ts:def:: LockRequest
@@ -609,6 +1338,8 @@ management.
}
+Removing products from inventory
+--------------------------------
.. http:delete:: /private/products/$PRODUCT_ID
@@ -617,11 +1348,11 @@ management.
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully deleted the product.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The backend does not know the instance or the product.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The backend refuses to delete the product because it is locked.
@@ -629,6 +1360,15 @@ management.
Payment processing
------------------
+To process Taler payments, a merchant must first setup an order with
+the merchant backend. The order is then claimed by a wallet, and
+paid by the wallet. The merchant can check the payment status of the
+order. Once the order is paid, the merchant may (for a limited time)
+grant refunds on the order.
+
+Creating orders
+---------------
+
.. _post-order:
.. http:post:: /private/orders
@@ -652,17 +1392,19 @@ Payment processing
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The backend has successfully created the proposal. The response is a
:ts:type:`PostOrderResponse`.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The order given used products from the inventory, but those were not found
- in the inventory. Or the merchant instance is unknown. Details in the
+ in the inventory. Or the merchant instance is unknown (including possibly the instance being not configured for new orders). Details in the
error code. NOTE: no good way to find out which product is not in the
inventory, we MAY want to specify that in the reply.
- :status 410 Gone:
+ :http:statuscode:`409 Conflict`:
+ A different proposal already exists under the specified order ID.
+ :http:statuscode:`410 Gone`:
The order given used products from the inventory that are out of stock.
- The reponse is a :ts:type:`OutOfStockResponse`.
+ The response is a :ts:type:`OutOfStockResponse`.
.. ts:def:: PostOrderRequest
@@ -773,13 +1515,7 @@ Payment processing
.. ts:def:: OutOfStockResponse
interface OutOfStockResponse {
- // Which items are out of stock?
- missing_products: OutOfStockEntry;
- }
-
- .. ts:def:: OutOfStockEntry
- interface OutOfStockEntry {
// Product ID of an out-of-stock item
product_id: string;
@@ -795,6 +1531,8 @@ Payment processing
}
+Inspecting orders
+-----------------
.. http:get:: /private/orders
@@ -812,7 +1550,7 @@ Payment processing
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The response is an `OrderHistory`.
.. ts:def:: OrderHistory
@@ -853,395 +1591,14 @@ Payment processing
paid: boolean;
}
-
-
-.. http:post:: /orders/$ORDER_ID/claim
-
- Wallet claims ownership (via nonce) over an order. By claiming
- an order, the wallet obtains the full contract terms, and thereby
- implicitly also the hash of the contract terms it needs for the
- other ``/public/`` APIs to authenticate itself as the wallet that
- is indeed eligible to inspect this particular order's status.
-
- **Request:**
-
- The request must be a `ClaimRequest`
-
- .. ts:def:: ClaimRequest
-
- interface ClaimRequest {
- // Nonce to identify the wallet that claimed the order.
- nonce: string;
-
- // Token that authorizes the wallet to claim the order.
- // *Optional* as the merchant may not have required it
- // (``create_token`` set to ``false`` in `PostOrderRequest`).
- token?: ClaimToken;
- }
-
- **Response:**
-
- :status 200 OK:
- The client has successfully claimed the order.
- The response contains the :ref:`contract terms <contract-terms>`.
- :status 404 Not found:
- The backend is unaware of the instance or order.
- :status 409 Conflict:
- The someone else claimed the same order ID with different nonce before.
-
- .. ts:def:: ClaimResponse
-
- interface ClaimResponse {
- // Contract terms of the claimed order
- contract_terms: ContractTerms;
-
- // Signature by the merchant over the contract terms.
- sig: EddsaSignature;
- }
-
-
-.. http:post:: /orders/$ORDER_ID/pay
-
- Pay for an order by giving a deposit permission for coins. Typically used by
- the customer's wallet. Note that this request does not include the
- usual ``h_contract`` argument to authenticate the wallet, as the hash of
- the contract is implied by the signatures of the coins. Furthermore, this
- API doesn't really return useful information about the order.
-
- **Request:**
-
- The request must be a `pay request <PayRequest>`.
-
- **Response:**
-
- :status 200 OK:
- The exchange accepted all of the coins.
- The body is a `payment response <PaymentResponse>`.
- The ``frontend`` should now fulfill the contract.
- Note that it is possible that refunds have been granted.
- :status 400 Bad request:
- Either the client request is malformed or some specific processing error
- happened that may be the fault of the client as detailed in the JSON body
- of the response.
- :status 402 Payment required:
- There used to be a sufficient payment, but due to refunds the amount effectively
- paid is no longer sufficient. (If the amount is generally insufficient, we
- return "406 Not Acceptable", only if this is because of refunds we return 402.)
- :status 403 Forbidden:
- One of the coin signatures was not valid.
- :status 404 Not found:
- The merchant backend could not find the order or the instance
- and thus cannot process the payment.
- :status 406 Not Acceptable:
- The payment is insufficient (sum is below the required total amount).
- :status 408 Request Timeout:
- The backend took too long to process the request. Likely the merchant's connection
- to the exchange timed out. Try again.
- :status 409 Conflict:
- The exchange rejected the payment because a coin was already spent before.
- The response will include the ``coin_pub`` for which the payment failed,
- in addition to the response from the exchange to the ``/deposit`` request.
- :status 410 Gone:
- The offer has expired and is no longer available.
- :status 412 Precondition Failed:
- The given exchange is not acceptable for this merchant, as it is not in the
- list of accepted exchanges and not audited by an approved auditor.
- :status 424 Failed Dependency:
- The merchant's interaction with the exchange failed in some way.
- The client might want to try later again.
- This includes failures like the denomination key of a coin not being
- known to the exchange as far as the merchant can tell.
-
- The backend will return verbatim the error codes received from the exchange's
- :ref:`deposit <deposit>` API. If the wallet made a mistake, like by
- double-spending for example, the frontend should pass the reply verbatim to
- the browser/wallet. If the payment was successful, the frontend MAY use
- this to trigger some business logic.
-
- .. ts:def:: PaymentResponse
-
- interface PaymentResponse {
- // Signature on ``TALER_PaymentResponsePS`` with the public
- // key of the merchant instance.
- sig: EddsaSignature;
-
- }
-
- .. ts:def:: PayRequest
-
- interface PayRequest {
- // The coins used to make the payment.
- coins: CoinPaySig[];
-
- // The session for which the payment is made (or replayed).
- // Only set for session-based payments.
- session_id?: string;
-
- }
-
- .. ts:def:: CoinPaySig
-
- export interface CoinPaySig {
- // Signature by the coin.
- coin_sig: EddsaSignature;
-
- // Public key of the coin being spend.
- coin_pub: EddsaPublicKey;
-
- // Signature made by the denomination public key.
- ub_sig: RsaSignature;
-
- // The hash of the denomination public key associated with this coin.
- h_denom: HashCode;
-
- // The amount that is subtracted from this coin with this payment.
- contribution: Amount;
-
- // URL of the exchange this coin was withdrawn from.
- exchange_url: string;
- }
-
-
-.. http:post:: /orders/$ORDER_ID/paid
-
- Prove that the client previously paid for an order by providing
- the merchant's signature from the `payment response <PaymentResponse>`.
- Typically used by the customer's wallet if it receives a request for
- payment for an order that it already paid. This is more compact then
- re-transmitting the full payment details.
- Note that this request does include the
- usual ``h_contract`` argument to authenticate the wallet and
- to allow the merchant to verify the signature before checking
- with its own database.
-
- **Request:**
-
- The request must be a `paid request <PaidRequest>`.
-
- **Response:**
-
- :status 204 No content:
- The merchant accepted the signature.
- The ``frontend`` should now fulfill the contract.
- Note that it is possible that refunds have been granted.
- :status 400 Bad request:
- Either the client request is malformed or some specific processing error
- happened that may be the fault of the client as detailed in the JSON body
- of the response.
- :status 403 Forbidden:
- The signature was not valid.
- :status 404 Not found:
- The merchant backend could not find the order or the instance
- and thus cannot process the request.
- :status 409 Conflict:
- The provided contract hash does not match this order.
-
- .. ts:def:: PaidRequest
-
- interface PaidRequest {
- // Signature on ``TALER_PaymentResponsePS`` with the public
- // key of the merchant instance.
- sig: EddsaSignature;
-
- // hash of the order's contract terms (this is used to authenticate the
- // wallet/customer and to enable signature verification without
- // database access).
- h_contract: HashCode;
-
- // Session id for which the payment is proven.
- session_id: string;
- }
-
-.. _order-abort:
-.. http:post:: /orders/$ORDER_ID/abort
-
- Abort paying for an order and obtain a refund for coins that
- were already deposited as part of a failed payment.
-
- **Request:**
-
- The request must be an `abort request <AbortRequest>`. We force the wallet
- to specify the affected coins as it may only request for a subset of the coins
- (i.e. because the wallet knows that some were double-spent causing the failure).
- Also we need to know the coins because there may be two wallets "competing" over
- the same order and one wants to abort while the other still proceeds with the
- payment. Here we need to again know which subset of the deposits to abort.
-
- **Response:**
-
- :status 200 OK:
- The merchant accepted the request, and passed it on to the exchange. The body is a
- a `merchant refund response <MerchantRefundResponse>`. Note that the exchange
- MAY still have encountered errors in processing. Those will then be part of
- the body. Wallets MUST carefully consider errors for each of the coins as
- returned by the exchange.
- :status 400 Bad request:
- Either the client request is malformed or some specific processing error
- happened that may be the fault of the client as detailed in the JSON body
- of the response.
- :status 403 Forbidden:
- The ``h_contract`` does not match the $ORDER_ID.
- :status 404 Not found:
- The merchant backend could not find the order or the instance
- and thus cannot process the abort request.
- :status 408 Request Timeout:
- The merchant backend took too long getting a response from the exchange.
- The wallet SHOULD retry soon.
- :status 412 Precondition Failed:
- Aborting the payment is not allowed, as the original payment did succeed.
- It is possible that a different wallet succeeded with the payment. This
- wallet should thus try to refresh all of the coins involved in the payment.
- :status 424 Failed Dependency:
- The merchant's interaction with the exchange failed in some way.
- The error from the exchange is included.
-
- The backend will return an `abort response <AbortResponse>`, which includes
- verbatim the error codes received from the exchange's
- :ref:`refund <exchange_refund>` API. The frontend should pass the replies verbatim to
- the browser/wallet.
-
- .. ts:def:: AbortRequest
-
- interface AbortRequest {
-
- // hash of the order's contract terms (this is used to authenticate the
- // wallet/customer in case $ORDER_ID is guessable).
- h_contract: HashCode;
-
- // List of coins the wallet would like to see refunds for.
- // (Should be limited to the coins for which the original
- // payment succeeded, as far as the wallet knows.)
- coins: AbortingCoin[];
- }
-
- .. ts:def:: AbortingCoin
-
- interface AbortingCoin {
- // Public key of a coin for which the wallet is requesting an abort-related refund.
- coin_pub: EddsaPublicKey;
-
- // The amount to be refunded (matches the original contribution)
- contribution: Amount;
-
- // URL of the exchange this coin was withdrawn from.
- exchange_url: string;
- }
-
-
- .. ts:def:: AbortResponse
-
- interface AbortResponse {
-
- // List of refund responses about the coins that the wallet
- // requested an abort for. In the same order as the 'coins'
- // from the original request.
- // The rtransaction_id is implied to be 0.
- refunds: MerchantAbortPayRefundStatus[];
- }
-
- .. ts:def:: MerchantAbortPayRefundStatus
-
- type MerchantAbortPayRefundStatus =
- | MerchantAbortPayRefundSuccessStatus
- | MerchantAbortPayRefundFailureStatus;
-
- .. ts:def:: MerchantAbortPayRefundFailureStatus
-
- // Details about why a refund failed.
- interface MerchantAbortPayRefundFailureStatus {
- // Used as tag for the sum type RefundStatus sum type.
- type: "failure"
-
- // HTTP status of the exchange request, must NOT be 200.
- exchange_status: Integer;
-
- // Taler error code from the exchange reply, if available.
- exchange_code?: Integer;
-
- // If available, HTTP reply from the exchange.
- exchange_reply?: Object;
- }
-
- .. ts:def:: MerchantAbortPayRefundSuccessStatus
-
- // Additional details needed to verify the refund confirmation signature
- // (``h_contract_terms`` and ``merchant_pub``) are already known
- // to the wallet and thus not included.
- interface MerchantAbortPayRefundSuccessStatus {
- // Used as tag for the sum type MerchantCoinRefundStatus sum type.
- type: "success"
-
- // HTTP status of the exchange request, 200 (integer) required for refund confirmations.
- exchange_status: 200;
-
- // the EdDSA :ref:`signature` (binary-only) with purpose
- // `TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND` using a current signing key of the
- // exchange affirming the successful refund
- exchange_sig: EddsaSignature;
-
- // public EdDSA key of the exchange that was used to generate the signature.
- // Should match one of the exchange's signing keys from /keys. It is given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- exchange_pub: EddsaPublicKey;
- }
-
-
-.. http:patch:: /private/orders/$ORDER_ID/forget
-
- Forget fields in an order's contract terms that the merchant no
- longer needs.
-
- **Request:**
-
- The request must be a `forget request <ForgetRequest>`. The fields specified
- must have been marked as forgettable when the contract was created. Fields in
- the request that are not in the `contract terms <ContractTerms>` are ignored.
-
- A valid
- JSON path is defined as a string beginning with ``$.`` that follows the dot
- notation: ``$.wire_fee``, for example. The ``$`` represents the `contract terms <ContractTerms>`
- object, and an identifier following a ``.`` represents the field of that
- identifier belonging to the object preceding the dot. Arrays can be indexed
- by an non-negative integer within brackets: ``$.products[1]``. An asterisk ``*``
- can be used to index an array as a wildcard, which expands the path into a
- list of paths containing one path for
- each valid array index: ``$.products[*].description``. For a path to be valid,
- it must end with a reference to a field of an object (it cannot end with an
- array index or wildcard).
-
- **Response:**
-
- :status 200 OK:
- The merchant deleted the specified fields from the contract of
- order $ORDER_ID.
- :status 400 Bad request:
- The request is malformed or one of the paths is invalid.
- :status 404 Not found:
- The merchant backend could not find the order or the instance
- and thus cannot process the abort request.
- :status 409 Conflict:
- The request includes a field that was not marked as forgettable, so
- the merchant cannot delete that field.
-
- .. ts:def:: ForgetRequest
-
- interface ForgetRequest {
-
- // Array of valid JSON paths to forgettable fields in the order's
- // contract terms.
- fields: string[];
- }
-
-
.. http:get:: /private/orders/$ORDER_ID
- Merchant checks the payment status of an order. If the order exists but is not payed
+ Merchant checks the payment status of an order. If the order exists but is not paid
and not claimed yet, the response provides a redirect URL. When the user goes to this URL,
- they will be prompted for payment. Differs from the ``/public/`` API both
+ they will be prompted for payment. Differs from the ``public`` API both
in terms of what information is returned and in that the wallet must provide
the contract hash to authenticate, while for this API we assume that the
- merchant is authenticated (as the endpoint is not ``/public/``).
+ merchant is authenticated (as the endpoint is not ``public``).
**Request:**
@@ -1251,11 +1608,11 @@ Payment processing
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
Returns a `MerchantOrderStatusResponse`, whose format can differ based on the status of the payment.
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The order or instance is unknown to the backend.
- :status 424 Failed dependency:
+ :http:statuscode:`424 Failed dependency`:
We failed to obtain a response from the exchange (about the wire transfer status).
.. ts:def:: MerchantOrderStatusResponse
@@ -1375,7 +1732,7 @@ Payment processing
// execution time of the wire transfer
execution_time: Timestamp;
- // Total amount that has been wire transfered
+ // Total amount that has been wire transferred
// to the merchant
amount: Amount;
@@ -1403,101 +1760,62 @@ Payment processing
coin_pub: CoinPublicKey;
}
+
+Private order data cleanup
+--------------------------
-.. http:get:: /orders/$ORDER_ID
-
- Query the payment status of an order. This endpoint is for the wallet.
- When the wallet goes to this URL and it is unpaid,
- they will be prompted for payment.
- This endpoint typically also supports requests with the "Accept" header
- requesting "text/html". In this case, an HTML response suitable for
- triggering the interaction with the wallet is returned, with ``timeout_ms``
- ignored (treated as zero). If the backend installation does not include the
- required HTML templates, a 406 status code is returned.
-
- In the case that the request was made with a claim token (even the wrong one)
- and the order was claimed and paid, the server will redirect the client to
- the fulfillment URL. This redirection will happen with a 302 status code
- if the "Accept" header specified "text/html", and with a 202 status code
- otherwise.
-
- **Request:**
-
- :query h_contract=HASH: hash of the order's contract terms (this is used to authenticate the wallet/customer in case $ORDER_ID is guessable). Required once an order was claimed.
- :query token=TOKEN: *Optional*. Authorizes the request via the claim token that was returned in the `PostOrderResponse`. Used with unclaimed orders only. Whether token authorization is required is determined by the merchant when the frontend creates the order.
- :query session_id=STRING: *Optional*. Session ID that the payment must be bound to. If not specified, the payment is not session-bound.
- :query timeout_ms=NUMBER: *Optional.* If specified, the merchant backend will
- wait up to ``timeout_ms`` milliseconds for completion of the payment before
- sending the HTTP response. A client must never rely on this behavior, as the
- merchant backend may return a response immediately.
- :query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund above the given AMOUNT. Only useful in combination with timeout.
- :query await_refund_obtained=BOOLEAN: *Optional*. If set to "yes", poll for the order's pending refunds to be picked up.
-
- **Response:**
-
- :status 200 OK:
- The response is a `StatusPaidResponse`.
- :status 202 Accepted:
- The response is a `StatusGotoResponse`. Only returned if the content type requested was not HTML.
- :status 302 Found:
- The client should go to the indicated location. Only returned if the content type requested was HTML.
- :status 402 PaymentRequired:
- The response is a `StatusUnpaidResponse`.
- :status 403 Forbidden:
- The ``h_contract`` (or the ``token`` for unclaimed orders) does not match the order
- and we have no fulfillment URL in the contract.
- :status 410 Gone:
- The response is a `StatusGoneResponse`.
- :status 404 Not found:
- The merchant backend is unaware of the order.
- :status 406 Not Acceptable:
- The merchant backend could not load the template required to generate a reply in the desired format. (Likely HTML templates were not properly installed.)
-
- .. ts:def:: StatusPaidResponse
+Some orders may contain sensitive information that the merchant may not want
+to retain after fulfillment, such as the customer's shipping address. By
+initially labeling these order components as forgettable, the merchant can
+later tell the backend to forget those details (without changing the hash of
+the contract!) to minimize risks from information leakage.
- interface StatusPaid {
- // Was the payment refunded (even partially, via refund or abort)?
- refunded: boolean;
- // Is any amount of the refund still waiting to be picked up (even partially)
- refund_pending: boolean;
+.. http:patch:: /private/orders/$ORDER_ID/forget
- // Amount that was refunded in total.
- refund_amount: Amount;
- }
+ Forget fields in an order's contract terms that the merchant no
+ longer needs.
- .. ts:def:: StatusGotoResponse
+ **Request:**
- interface StatusGotoResponse {
- // The client should go to the fulfillment URL, it may be ready or
- // might have some other interesting status.
- fulfillment_url: string;
- }
+ The request must be a `forget request <ForgetRequest>`. The fields specified
+ must have been marked as forgettable when the contract was created. Fields in
+ the request that are not in the `contract terms <ContractTerms>` are ignored.
- .. ts:def:: StatusUnpaidResponse
+ A valid
+ JSON path is defined as a string beginning with ``$.`` that follows the dot
+ notation: ``$.wire_fee``, for example. The ``$`` represents the `contract terms <ContractTerms>`
+ object, and an identifier following a ``.`` represents the field of that
+ identifier belonging to the object preceding the dot. Arrays can be indexed
+ by an non-negative integer within brackets: ``$.products[1]``. An asterisk ``*``
+ can be used to index an array as a wildcard, which expands the path into a
+ list of paths containing one path for
+ each valid array index: ``$.products[*].description``. For a path to be valid,
+ it must end with a reference to a field of an object (it cannot end with an
+ array index or wildcard).
- interface StatusUnpaidResponse {
- // URI that the wallet must process to complete the payment.
- taler_pay_uri: string;
+ **Response:**
- // Status URL, can be used as a redirect target for the browser
- // to show the order QR code / trigger the wallet.
- fulfillment_url?: string;
+ :http:statuscode:`200 OK`:
+ The merchant deleted the specified fields from the contract of
+ order $ORDER_ID.
+ :http:statuscode:`400 Bad request`:
+ The request is malformed or one of the paths is invalid.
+ :http:statuscode:`404 Not found`:
+ The merchant backend could not find the order or the instance
+ and thus cannot process the abort request.
+ :http:statuscode:`409 Conflict`:
+ The request includes a field that was not marked as forgettable, so
+ the merchant cannot delete that field.
- // Alternative order ID which was paid for already in the same session.
- // Only given if the same product was purchased before in the same session.
- already_paid_order_id?: string;
- }
+ .. ts:def:: ForgetRequest
- .. ts:def:: StatusGoneResponse
+ interface ForgetRequest {
- // The client tried to access the order via the claim
- // token (and not a valid h_contract), but the order can't be claimed
- // anymore, as it is already paid.
- interface StatusGoneResponse {
- // Fulfillment URL for the order.
- fulfillment_url: string;
+ // Array of valid JSON paths to forgettable fields in the order's
+ // contract terms.
+ fields: string[];
}
@@ -1509,11 +1827,11 @@ Payment processing
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully deleted the order.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The backend does not know the instance or the order.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The backend refuses to delete the order.
@@ -1534,16 +1852,16 @@ Giving Refunds
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The refund amount has been increased, the backend responds with a `MerchantRefundResponse`
- :status 403 Forbidden:
+ :http:statuscode:`403 Forbidden`:
For the given order, the refund delay was zero and thus refunds are categorically not allowed.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The order is unknown to the merchant
- :status 410 Gone:
+ :http:statuscode:`410 Gone`:
It is too late for aborting, the exchange may have already wired the funds
to the merchant.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The refund amount exceeds the amount originally paid
.. ts:def:: RefundRequest
@@ -1571,119 +1889,18 @@ Giving Refunds
}
-.. http:post:: /orders/$ORDER_ID/refund
-
- Obtain refunds for an order. After talking to the exchange, the refunds will
- no longer be pending if processed successfully.
-
- **Request:**
-
- The request body is a `WalletRefundRequest` object.
-
- **Response:**
-
- :status 200 OK:
- The response is a `WalletRefundResponse`.
- :status 204 No content:
- There are no refunds for the order.
- :status 403 Forbidden:
- The ``h_contract`` does not match the order.
- :status 404 Not found:
- The merchant backend is unaware of the order.
-
- .. ts:def:: WalletRefundRequest
-
- interface WalletRefundRequest {
- // hash of the order's contract terms (this is used to authenticate the
- // wallet/customer).
- h_contract: HashCode;
- }
-
- .. ts:def:: WalletRefundResponse
-
- interface WalletRefundResponse {
- // Amount that was refunded in total.
- refund_amount: Amount;
-
- // Successful refunds for this payment, empty array for none.
- refunds: MerchantCoinRefundStatus[];
-
- // Public key of the merchant.
- merchant_pub: EddsaPublicKey;
-
- }
-
- .. ts:def:: MerchantCoinRefundStatus
-
- type MerchantCoinRefundStatus =
- | MerchantCoinRefundSuccessStatus
- | MerchantCoinRefundFailureStatus;
-
- .. ts:def:: MerchantCoinRefundFailureStatus
-
- // Details about why a refund failed.
- interface MerchantCoinRefundFailureStatus {
- // Used as tag for the sum type RefundStatus sum type.
- type: "failure";
-
- // HTTP status of the exchange request, must NOT be 200.
- exchange_status: Integer;
-
- // Taler error code from the exchange reply, if available.
- exchange_code?: Integer;
-
- // If available, HTTP reply from the exchange.
- exchange_reply?: Object;
-
- // Refund transaction ID.
- rtransaction_id: Integer;
-
- // public key of a coin that was refunded
- coin_pub: EddsaPublicKey;
-
- // Amount that was refunded, including refund fee charged by the exchange
- // to the customer.
- refund_amount: Amount;
- }
-
- .. ts:def:: MerchantCoinRefundSuccessStatus
-
- // Additional details needed to verify the refund confirmation signature
- // (``h_contract_terms`` and ``merchant_pub``) are already known
- // to the wallet and thus not included.
- interface MerchantCoinRefundSuccessStatus {
- // Used as tag for the sum type MerchantCoinRefundStatus sum type.
- type: "success";
-
- // HTTP status of the exchange request, 200 (integer) required for refund confirmations.
- exchange_status: 200;
-
- // the EdDSA :ref:`signature` (binary-only) with purpose
- // `TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND` using a current signing key of the
- // exchange affirming the successful refund
- exchange_sig: EddsaSignature;
-
- // public EdDSA key of the exchange that was used to generate the signature.
- // Should match one of the exchange's signing keys from /keys. It is given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- exchange_pub: EddsaPublicKey;
-
- // Refund transaction ID.
- rtransaction_id: Integer;
+-----------------------
+Tracking Wire Transfers
+-----------------------
- // public key of a coin that was refunded
- coin_pub: EddsaPublicKey;
+This API is used by merchants that want to track the payments from the
+exchange to be sure that they have been paid on time. By telling the merchant
+backend about all incoming wire transfers, the backend can detect if an
+exchange failed to perform a wire transfer that was due.
- // Amount that was refunded, including refund fee charged by the exchange
- // to the customer.
- refund_amount: Amount;
- }
-
-------------------------
-Tracking Wire Transfers
-------------------------
+Informing the backend about incoming wire transfers
+---------------------------------------------------
.. http:post:: /private/transfers
@@ -1698,10 +1915,10 @@ Tracking Wire Transfers
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The wire transfer is known to the exchange, details about it follow in the body.
The body of the response is a `MerchantTrackTransferResponse`.
- :status 202 Accepted:
+ :http:statuscode:`202 Accepted`:
The exchange provided conflicting information about the transfer. Namely,
there is at least one deposit among the deposits aggregated by ``wtid``
that accounts for a coin whose
@@ -1715,12 +1932,12 @@ Tracking Wire Transfers
the bad behavior to the auditor -- and then hope for the auditor to
resolve it. So in that respect, 202 is the right status code as more
work remains to be done for a final resolution.)
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The instance is unknown to the exchange.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The wire transfer identifier is already known to us, but for a different amount,
wire method or exchange.
- :status 424 Failed Dependency:
+ :http:statuscode:`424 Failed dependency`:
The exchange returned an error when we asked it about the "GET /transfer" status
for this wire transfer. Details of the exchange error are returned.
@@ -1821,7 +2038,7 @@ Tracking Wire Transfers
// Master public key of the exchange
master_pub: EddsaPublicKey;
- }
+ }
.. ts:def:: TrackTransferConflictDetails
@@ -1905,6 +2122,8 @@ Tracking Wire Transfers
}
+Querying known wire transfers
+-----------------------------
.. http:get:: /private/transfers
@@ -1934,7 +2153,7 @@ Tracking Wire Transfers
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The body of the response is a `TransferList`.
.. ts:def:: TransferList
@@ -1979,12 +2198,32 @@ Tracking Wire Transfers
}
-
-
--------------------
-Giving Customer Tips
+Backend: Giving tips
--------------------
+Tips are a way for websites to give small amounts of e-cash to visitors (for
+example as a financial reward for providing information or watching
+advertisements). Tips are non-contractual: neither merchant nor consumer
+have any contractual information about the other party as a result of the
+tip.
+
+
+Create reserve
+--------------
+
+Reserves are basically funds a merchant has provided
+to an exchange for a tipping campaign. Each reserve
+has a limited lifetime (say 2--4 weeks). Any funds
+not used to tip customers will automatically be wired
+back from the exchange to the originating account.
+
+To begin tipping, a merchant must tell the backend
+to setup a reserve. The backend will return a
+reserve public key which must be used as the wire
+transfer subject when wiring the tipping campaign
+funds to the exchange.
+
.. _tips:
.. http:post:: /private/reserves
@@ -2002,14 +2241,14 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The backend is waiting for the reserve to be established. The merchant
must now perform the wire transfer indicated in the `ReserveCreateConfirmation`.
- :status 408 Request Timeout:
+ :http:statuscode:`408 Request timeout`:
The exchange did not respond on time.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The exchange does not support the requested wire method.
- :status 424 Failed Dependency:
+ :http:statuscode:`424 Failed dependency`:
We could not obtain /wire details from the specified exchange base URL.
.. ts:def:: ReserveCreateRequest
@@ -2047,7 +2286,7 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
Returns a list of known tipping reserves.
The body is a `TippingReserveStatus`.
@@ -2087,6 +2326,9 @@ Giving Customer Tips
active: boolean;
}
+
+Query funds remaining
+---------------------
.. http:get:: /private/reserves/$RESERVE_PUB
@@ -2098,11 +2340,11 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
Returns the `ReserveDetail`.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The tipping reserve is not known.
- :status 424 Failed Dependency:
+ :http:statuscode:`424 Failed dependency`:
We are having trouble with the request because of a problem with the exchange.
Likely returned with an "exchange_code" in addition to a "code" and
an "exchange_http_status" in addition to our own HTTP status. Also usually
@@ -2156,6 +2398,9 @@ Giving Customer Tips
}
+Authorizing tips
+----------------
+
.. http:post:: /private/reserves/$RESERVE_PUB/authorize-tip
Authorize creation of a tip from the given reserve.
@@ -2166,11 +2411,11 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
A tip has been created. The backend responds with a `TipCreateConfirmation`
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The instance or the reserve is unknown to the backend.
- :status 412 Precondition Failed:
+ :http:statuscode:`412 Precondition failed`:
The tip amount requested exceeds the available reserve balance for tipping.
.. ts:def:: TipCreateRequest
@@ -2218,15 +2463,18 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
A tip has been created. The backend responds with a `TipCreateConfirmation`
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The instance is unknown to the backend.
- :status 412 Precondition Failed:
+ :http:statuscode:`412 Precondition failed`:
The tip amount requested exceeds the available reserve balance for tipping
in all of the reserves of the instance.
+Deleting reserves
+-----------------
+
.. http:delete:: /private/reserves/$RESERVE_PUB
Delete information about a reserve. Fails if the reserve still has
@@ -2241,14 +2489,16 @@ Giving Customer Tips
**Response:**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The backend has successfully deleted the reserve.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The backend does not know the instance or the reserve.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The backend refuses to delete the reserve (committed tips awaiting pickup).
+Checking tip status
+-------------------
.. http:get:: /private/tips/$TIP_ID
@@ -2260,9 +2510,9 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The tip is known. The backend responds with a `TipDetails` message
- :status 404 Not Found:
+ :http:statuscode:`404 Not found`:
The tip is unknown to the backend.
.. ts:def:: TipDetails
@@ -2302,46 +2552,6 @@ Giving Customer Tips
}
-
-.. http:get:: /tips/$TIP_ID
-
- Handle request from wallet to provide details about a tip.
-
- This endpoint typically also supports requests with the "Accept" header
- requesting "text/html". In this case, an HTML response suitable for
- triggering the interaction with the wallet is returned. If the backend
- installation does not include the required HTML templates, a 406 status
- code is returned.
-
- **Response:**
-
- :status 200 OK:
- A tip is being returned. The backend responds with a `TipInformation`.
- :status 404 Not Found:
- The tip identifier is unknown.
- :status 406 Not Acceptable:
- The merchant backend could not load the template required to generate a reply in the desired format. (Likely HTML templates were not properly installed.)
- :status 410 Gone:
- A tip has been fully claimed. The JSON reply still contains the `TipInformation`.
-
- .. ts:def:: TipInformation
-
- interface TipInformation {
-
- // Exchange from which the tip will be withdrawn. Needed by the
- // wallet to determine denominations, fees, etc.
- exchange_url: string;
-
- // (remaining) amount of the tip (including fees).
- tip_amount: Amount;
-
- // Timestamp indicating when the tip is set to expire (may be in the past).
- // Note that tips that have expired MAY also result in a 404 response.
- expiration: Timestamp;
- }
-
-
-
.. http:get:: /private/tips
Return the list of all tips.
@@ -2356,7 +2566,7 @@ Giving Customer Tips
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The backend has successfully found the list of tips. The backend responds
with a `TipsResponse`.
@@ -2384,68 +2594,17 @@ Giving Customer Tips
-.. http:post:: /tips/$TIP_ID/pickup
-
- Handle request from wallet to pick up a tip.
-
- **Request:**
-
- The request body is a `TipPickupRequest` object.
-
- **Response:**
-
- :status 200 OK:
- A tip is being returned. The backend responds with a `TipResponse`
- :status 401 Unauthorized:
- The tip amount requested exceeds the tip.
- :status 404 Not Found:
- The tip identifier is unknown.
- :status 409 Conflict:
- Some of the denomination key hashes of the request do not match those currently available from the exchange (hence there is a conflict between what the wallet requests and what the merchant believes the exchange can provide).
- :status 410 Gone:
- The tip has expired.
-
- .. ts:def:: TipPickupRequest
-
- interface TipPickupRequest {
-
- // List of planches the wallet wants to use for the tip
- planchets: PlanchetDetail[];
- }
-
- .. ts:def:: PlanchetDetail
-
- interface PlanchetDetail {
- // Hash of the denomination's public key (hashed to reduce
- // bandwidth consumption)
- denom_pub_hash: HashCode;
-
- // coin's blinded public key
- coin_ev: CoinEnvelope;
- }
-
- .. ts:def:: TipResponse
-
- interface TipResponse {
-
- // Blind RSA signatures over the planchets.
- // The order of the signatures matches the planchets list.
- blind_sigs: BlindSignature[];
- }
-
- .. ts:def:: BlindSignature
-
- interface BlindSignature {
-
- // The (blind) RSA signature. Still needs to be unblinded.
- blind_sig: BlindedRsaSignature;
- }
-
-
------------------
The Contract Terms
------------------
+This section describes the overall structure of
+the contract terms that are the foundation for
+Taler payments.
+
+FIXME: the "forgettable" attribute is not
+properly specified here!
+
.. _contract-terms:
The contract terms must have the following structure:
@@ -2637,7 +2796,7 @@ The `Product` object describes the product being purchased from the merchant. It
.. ts:def:: Location
- // Delivery location, losely modeled as a subset of
+ // Delivery location, loosely modeled as a subset of
// ISO20022's PostalAddress25.
interface Location {
// Nation with its own government.
diff --git a/core/api-sync.rst b/core/api-sync.rst
index 67d1e214..1e673ed6 100644
--- a/core/api-sync.rst
+++ b/core/api-sync.rst
@@ -94,7 +94,7 @@ itself cannot not enforce these rules.
increment it by the smallest possible amount when uploading an
update.
* In general, the merge operation should be implemented in such a way
- that it deals gracefully with adversarial devices from rouge
+ that it deals gracefully with adversarial devices from rogue
devices connected to the same account.
It is assumed that the synchronization service is only ever accessed
@@ -156,28 +156,28 @@ Receiving Terms of Service
**Response**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The body contains the current version of the backup
as known to the server.
- :status 204 No content:
+ :http:statuscode:`204 No content`:
This is a fresh account, no previous backup data exists at
the server.
- :status 304 Not modified:
+ :http:statuscode:`304 Not modified`:
The version available at the server is identical to that
specified in the "If-None-Match" header.
- :status 404 Not found:
+ :http:statuscode:`404 Not found`:
The backup service is unaware of a matching account.
- :status 410 Gone:
+ :http:statuscode:`410 Gone`:
The backup service has closed operations. The body will
contain the latest version still available at the server.
The body may be empty if no version is available.
The user should be urged to find another provider.
- :status 429 Too many requests:
+ :http:statuscode:`429 Too many requests`:
This account has exceeded thresholds for the number of
requests. The client should try again later, and may want
to decrease its synchronization frequency.
@@ -186,7 +186,7 @@ Receiving Terms of Service
"200 OK" responses include an HTTP header
"Sync-Signature" with the signature of the
- client from the orginal upload, and an
+ client from the original upload, and an
"Sync-Previous" with the version that was
being updated (unless this is the first revision).
"Sync-Previous" is only given to enable
@@ -249,52 +249,52 @@ Receiving Terms of Service
**Response**
- :status 204 No content:
+ :http:statuscode:`204 No content`:
The transfer was successful, and the server has registered
the new version.
- :status 304 Not modified:
+ :http:statuscode:`304 Not modified`:
The server is already aware of this version of the client.
Returned before 100 continue to avoid upload.
- :status 400 Bad request:
+ :http:statuscode:`400 Bad request`:
Most likely, the uploaded body is too short (less than 32 bytes).
- :status 402 Payment required:
+ :http:statuscode:`402 Payment required`:
The synchronization service requires payment before the
account can continue to be used. The fulfillment URL
should be the /$ACCOUNT-KEY URL, but can be safely ignored
by the client. The contract should be shown to the user
in the canonical dialog, possibly in a fresh tab.
- :status 403 Forbidden:
+ :http:statuscode:`403 Forbidden`:
The signature is invalid or missing (or body does not match).
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The server has a more recent version than what is given
in "If-Match". The more recent version is returned. The
client should merge the two versions and retry using the
given response's "E-Tag" in the next attempt in "If-Match".
- :status 410 Gone:
+ :http:statuscode:`410 Gone`:
The backup service has closed operations. The body will
contain the latest version still available at the server.
The body may be empty if no version is available.
The user should be urged to find another provider.
- :status 411 Length required:
+ :http:statuscode:`411 Length required`:
The client must specify the "Content-length" header before
attempting upload. While technically optional by the
HTTP specification, the synchronization service may require
the client to provide the length upfront.
- :status 413 Request Entity Too Large:
+ :http:statuscode:`413 Request entity too large`:
The requested upload exceeds the quota for the type of
account. The client should suggest to the user to
migrate to another backup and synchronization service
(like with "410 Gone").
- :status 429 Too many requests:
+ :http:statuscode:`429 Too many requests`:
This account has exceeded daily thresholds for the number of
requests. The client should try again later, and may want
to decrease its synchronization frequency.
@@ -303,7 +303,7 @@ Receiving Terms of Service
Responses with a body include an HTTP header
"Sync-Signature" with the signature of the
- client from the orginal upload, and an
+ client from the original upload, and an
"If-Match" with the version that is
being updated (unless this is the first revision).
@@ -337,7 +337,7 @@ auditors or exchanges.
The client should urge the user to make use of a synchronization
service upon first withdrawal, suggesting one that is free or
accepts payment in the respective currency. If none is available,
-the client should warn the user about the lack of availalable
+the client should warn the user about the lack of available
backups and synchronization and suggest to the user to find a
reasonable service. Once a synchronization service was selected,
the client should urge the user to print the respective key
diff --git a/core/api-wire.rst b/core/api-wire.rst
index fee78890..ce8c94e4 100644
--- a/core/api-wire.rst
+++ b/core/api-wire.rst
@@ -49,13 +49,16 @@ Making Transactions
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The request has been correctly handled, so the funds have been transferred to
the recipient's account. The body is a `TransferResponse`
- :status 400 Bad Request: Request malformed. The bank replies with an `ErrorDetail` object.
- :status 401 Unauthorized: Authentication failed, likely the credentials are wrong.
- :status 404 Not found: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
- :status 409 Conflict:
+ :http:statuscode:`400 Bad request`:
+ Request malformed. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`401 Unauthorized`:
+ Authentication failed, likely the credentials are wrong.
+ :http:statuscode:`404 Not found`:
+ The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`409 Conflict`:
A transaction with the same ``transaction_uid`` but different transaction details
has been submitted before.
@@ -91,7 +94,7 @@ Making Transactions
amount: Amount;
// Base URL of the exchange. Shall be included by the bank gateway
- // in the approriate section of the wire transfer details.
+ // in the appropriate section of the wire transfer details.
exchange_base_url: string;
// Wire transfer identifier chosen by the exchange,
@@ -156,10 +159,10 @@ Querying the transaction history
**Response**
- :status 200 OK: JSON object of type `IncomingHistory`.
- :status 400 Bad Request: Request malformed. The bank replies with an `ErrorDetail` object.
- :status 401 Unauthorized: Authentication failed, likely the credentials are wrong.
- :status 404 Not found: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`200 OK`: JSON object of type `IncomingHistory`.
+ :http:statuscode:`400 Bad request`: Request malformed. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`401 Unauthorized`: Authentication failed, likely the credentials are wrong.
+ :http:statuscode:`404 Not found`: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
.. ts:def:: IncomingHistory
@@ -238,10 +241,10 @@ Querying the transaction history
**Response**
- :status 200 OK: JSON object of type `OutgoingHistory`.
- :status 400 Bad Request: Request malformed. The bank replies with an `ErrorDetail` object.
- :status 401 Unauthorized: Authentication failed, likely the credentials are wrong.
- :status 404 Not found: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`200 OK`: JSON object of type `OutgoingHistory`.
+ :http:statuscode:`400 Bad request`: Request malformed. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`401 Unauthorized`: Authentication failed, likely the credentials are wrong.
+ :http:statuscode:`404 Not found`: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
.. ts:def:: OutgoingHistory
@@ -296,12 +299,15 @@ exposed by bank gateways in production.
**Response:**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The request has been correctly handled, so the funds have been transferred to
the recipient's account. The body is a `AddIncomingResponse`
- :status 400 Bad Request: The request is malformed. The bank replies with an `ErrorDetail` object.
- :status 401 Unauthorized: Authentication failed, likely the credentials are wrong.
- :status 404 Not found: The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`400 Bad request`:
+ The request is malformed. The bank replies with an `ErrorDetail` object.
+ :http:statuscode:`401 Unauthorized`:
+ Authentication failed, likely the credentials are wrong.
+ :http:statuscode:`404 Not found`:
+ The endpoint is wrong or the user name is unknown. The bank replies with an `ErrorDetail` object.
.. ts:def:: AddIncomingRequest
diff --git a/design-documents/002-wallet-exchange-management.rst b/design-documents/002-wallet-exchange-management.rst
index d70a799c..9d10045a 100644
--- a/design-documents/002-wallet-exchange-management.rst
+++ b/design-documents/002-wallet-exchange-management.rst
@@ -154,7 +154,7 @@ Response:
// The "reasonable-ness" of the exchange's fees.
feeStructureSummary: FeeStructureSummary | undefined;
- // Detailled info for each individual denomination
+ // Detailed info for each individual denomination
denominations: ExchangeDenomination[];
// Currency of the exchange.
diff --git a/design-documents/003-tos-rendering.rst b/design-documents/003-tos-rendering.rst
index 52d293c3..3011775c 100644
--- a/design-documents/003-tos-rendering.rst
+++ b/design-documents/003-tos-rendering.rst
@@ -34,7 +34,7 @@ Legal documents with mime type ``text/markdown`` **should** confirm to the
When wallets render ``text/markdown`` legal documents, they **must** disable
embedded HTML rendering. Wallets **may** style the markdown rendering to improve
-usability. For example, they can make sections collabsible or add a nagivation side-bar
+usability. For example, they can make sections collabsible or add a navigation side-bar
on bigger screens.
It is recommended that the ``text/markdown`` document is used as the "master
diff --git a/design-documents/005-wallet-backup-sync.rst b/design-documents/005-wallet-backup-sync.rst
index 26dbbf17..20fce37b 100644
--- a/design-documents/005-wallet-backup-sync.rst
+++ b/design-documents/005-wallet-backup-sync.rst
@@ -3,7 +3,11 @@ Design Doc 005: Wallet Backup and Sync
.. warning::
- This is an unfinished draft.
+ This document is deprecated. We have decided to first
+ implement backup, and tackle sync later.
+ The multi-device sync described in this document would lead
+ to a bad/unexpected user experience that does not justify the
+ conceptual / implementation complexity.
Summary
=======
diff --git a/design-documents/007-payment.rst b/design-documents/007-payment.rst
index 0095d1d7..9a16776d 100644
--- a/design-documents/007-payment.rst
+++ b/design-documents/007-payment.rst
@@ -40,8 +40,9 @@ When *resource-URL* is requested, the storefront runs the following steps:
1. Extract the *resource name* from the *resource-URL*.
2. Extract the *session-ID* (or null) from the request's validated cookie (for example, by using signed cookies).
3. Extract the *order-ID* (or null) from the request's ``order_id`` cookie. This cookie may optionally be validated.
-..
- is "invalid" equivalent to "null"?
+
+ ..
+ is "invalid" equivalent to "null"?
4. If *session-ID* or *order-ID* is null, assign a fresh session ID and
create a new order for *resource name* by doing a ``POST /private/orders``
diff --git a/design-documents/fees.rst b/design-documents/008-fees.rst
index 96459423..331d6cd7 100644
--- a/design-documents/fees.rst
+++ b/design-documents/008-fees.rst
@@ -1,4 +1,4 @@
-Design Doc 003: Fee Structure Metrics
+Design Doc 008: Fee Structure Metrics
#####################################
.. note::
@@ -48,7 +48,7 @@ cost for the user and and (2) restrictions on denomination/fee structures.
Thus the metrics should still allow some degree of variability between
exchanges.
-We make the assumption that wallet always perfer operations with better
+We make the assumption that wallet always prefer operations with better
privacy. For example, a single coin should only be used for at most one
spend operation and at most one refresh operation.
@@ -109,7 +109,7 @@ Drawbacks
=========
* The approach does not work well in some special-purpose deployments,
- where the coin structure is taylored to the products of merchants,
+ where the coin structure is tailored to the products of merchants,
and refreshing would never even happen.
* The approach also does not consider more "creative" fee structures,
diff --git a/design-documents/009-backup.rst b/design-documents/009-backup.rst
new file mode 100644
index 00000000..1b4895ee
--- /dev/null
+++ b/design-documents/009-backup.rst
@@ -0,0 +1,118 @@
+Design Doc 009: Wallet Backup
+#############################
+
+Summary
+=======
+
+This document describes the backup system used by Taler wallets.
+This is the second, simplified iteration of the proposal, which leaves
+out multi-device synchronization.
+
+
+Requirements
+============
+
+* Backup must work both with and without Anastasis
+
+ * When not using Anastasis, the user is responsible for keeping
+ their wallet's root secret safe.
+
+* Arbitrary number of backup providers must be supported
+* Minimize information leaks / timing side channels (user might be able to change
+ some setting to allow more frequent backup with less potential data loss but more
+ leakage)
+* Minimize potential to lose money or important information
+* Since real-time sync is not supported yet, wallets should have a feature
+ where their whole content is "emptied" to another wallet, and the wallet is
+ reset.
+* Even without real-time sync, the backup data must support merging with old, existing wallet
+ state, as the device that the wallet runs on may be restored from backup or be offline
+ for a long time.
+
+
+Solution Overview
+=================
+
+Each wallet has a 64 byte wallet root secret, which is used to derive all other secrets
+used during backup, which are currently:
+
+1. The sync account key for a sync provider, derived via the sync provider's base URL.
+2. The symmetric key used to encrypt the backup blob
+
+If the user chooses to use Anastasis, the following information is backed up in Anastasis:
+
+* list of used backup providers
+* wallet root secret
+
+
+Supported Operations
+--------------------
+
+* **restore-from-anastasis**: Start Anastasis recovery process. This requires
+ the wallet backup state to be "uninitialized".
+* **restore-from-recovery-secret**: This requires the wallet backup state to be uninitialized.
+* **add-provider** / **remove-provider**: Add/remove a sync provider from the
+ list of providers. Adding a provider will cause payment(s) to the provider
+ to be scheduled according to the provider's terms. If the wallet backup
+ state is "uninitialized", adding a provider will set the backup state to
+ "initialized" with a fresh wallet root key. Changing the provider list will
+ also update the backup provider URL list in the anastasis core secret.
+* **abandon** / **takeover**: When the user wants to stop using a wallet on a particular
+ device, another wallet can "take over" by reading the recovery secret of the abandoned wallet.
+ The abandoned wallet marks explicitly in its backup blob that it is abandoned.
+ Abandoning a wallet will set the backup state to "uninitialized".
+* **backup**: Do a backup cycle.
+* **rekey**: Change to a new wallet root secret, in case the old one has been
+ compromised. Only protectes future funds of the wallet from being
+ compromised. Requires a new payment to all configured backup providers.
+
+
+Backup Format
+-------------
+
+TBD. Considerations from :doc:`005-wallet-backup-sync` still apply,
+especially regarding the CRDT.
+
+
+Initial User Experience
+-----------------------
+
+The user will be asked to set up backup&sync (by selecting a provider)
+after the first withdrawal operation has been confirmed. After selecting
+the backup&sync providers, the user will be presented with a "checklist" that
+contains an option to (1) show/print the recovery secret and (2) set up Anastasis.
+
+The wallet will initially only withdraw enough money to pay the
+backup&sync/anastasis providers. Only after successful backup of the wallet's
+signed planchets, the full withdrawal will be completed.
+
+
+Open Questions
+==============
+
+* Should the wallet root secret and wallet database be locally encrypted
+ and protected via a passphrase?
+* What happens if the same Anastasis user has multiple wallets? Can Anastasis somehow
+ support multiple "instances" per application?
+
+Future Work / Ideas
+===================
+
+* Incremental backups?
+
+ * Instead of one big blob that always needs to be read/written, we could have (1) a
+ limited length append-only journal and (2) a merkle tree so that the backup blob can
+ be updated incrementally once the journal is full.
+ * Leaks more information and is more complex.
+
+* Mult-device synchronization, with synchronous communication either over some signaling server
+ or P2P connectivity (WebRTC, etc.)
+
+ * Destroys the "wallet" metaphor, now the wallet is more like an account.
+ * We should first agree on the requirements from the perspective of end users
+ * P2P payments in Taler might also make sync less important
+ * Maybe only parts of the state (purchases / contracts, but not coins) should be synchronized?
+ * WhatsApp web model: The wallet runs only on one devices, but other devices
+ can connect to it as clients. (Allows my browser wallet to temporarily access
+ money from my phone wallet and vice versa.)
+
diff --git a/design-documents/index.rst b/design-documents/index.rst
index fda8b2e5..55dd8c1e 100644
--- a/design-documents/index.rst
+++ b/design-documents/index.rst
@@ -17,3 +17,5 @@ and protocol.
005-wallet-backup-sync
006-anastasis-ux
007-payment
+ 008-fees
+ 009-backup
diff --git a/developers-manual.rst b/developers-manual.rst
index c5f5b771..22d205f0 100644
--- a/developers-manual.rst
+++ b/developers-manual.rst
@@ -965,7 +965,7 @@ Python for Scripting
When using Python for writing small utilities, the following libraries
are useful:
-* ``click`` for argument parsing (should be prefered over argparse)
+* ``click`` for argument parsing (should be preferred over argparse)
* ``pathlib`` for path manipulation (part of the standard library)
* ``subprocess`` for "shelling out" to other programs. Prefer ``subprocess.run``
over the older APIs.
@@ -1314,7 +1314,7 @@ use when talking to end users or even system administrators.
relative time
method of keeping time in :term:`GNUnet` where the time is represented
as a relative number of microseconds. Thus, a relative time specifies
- an offet or a duration, but not a date. Called relative time in
+ an offset or a duration, but not a date. Called relative time in
contrast to :term:`absolute time`.
recoup
@@ -1334,7 +1334,7 @@ use when talking to end users or even system administrators.
making a payment with :term:`coins` to a :term:`merchant`.
privacy policy
- Statment of an operator how they will protect the privacy of users.
+ Statement of an operator how they will protect the privacy of users.
proof
Message that cryptographically demonstrates that a particular claim is correct.
diff --git a/libeufin/api-nexus.rst b/libeufin/api-nexus.rst
index 1d35a4ff..c9fb6890 100644
--- a/libeufin/api-nexus.rst
+++ b/libeufin/api-nexus.rst
@@ -46,7 +46,7 @@ User Management
**Response:**
- :status 409 Conflict: Username is not available.
+ :http:statuscode:`409 Conflict`: Username is not available.
**Details:**
@@ -96,7 +96,7 @@ manages payment initiations of the account and tracks the initiations of payment
Ask nexus to submit one prepare payment at the bank.
- :status 404 Not Found: the unique identifier **or**
+ :http:statuscode:`404 Not found`: the unique identifier **or**
the bank connection could not be found in the system
@@ -111,7 +111,7 @@ manages payment initiations of the account and tracks the initiations of payment
interface PaymentStatus {
// Payment unique identifier
- uuid: string;
+ paymentInitiationId: string;
// True for submitted payments
submitted: boolean;
@@ -198,17 +198,38 @@ manages payment initiations of the account and tracks the initiations of payment
.. ts:def:: CollectedTransaction
interface CollectedTransaction {
- // Optional field to specify the bank connection to
- // use for such operation.
- bankConnection?: string;
- // dashed date (YYYY-MM-DD) of the earliest payment
- // in the result. Optional, defaults to "earliest
- // possible" date.
- start: string;
- // dashed date (YYYY-MM-DD) of the earliest payment
- // in the result. Optional, defaults to "latest
- // possible" date.
- end: string;
+
+ // This type indicates the time range of the query.
+ // It can assume the following values:
+ //
+ // 'latest': retrieves the last transactions from the bank.
+ // If there are older unread transactions, those will *not*
+ // be downloaded.
+ //
+ // 'all': retrieves all the transactions from the bank,
+ // until the oldest.
+ //
+ // 'previous-days': currently *not* implemented, it will allow
+ // the request to download transactions from
+ // today until N days before.
+ //
+ // 'since-last': retrieves all the transactions since the last
+ // time one was downloaded.
+ //
+ rangeType: string;
+
+ // Because transactions are delivered by banks in "batches",
+ // then every batch can have different qualities. This value
+ // lets the request specify which type of batch ought to be
+ // returned. Currently, the following two type are supported:
+ //
+ // 'report': intra-day information
+ // 'statement': prior day bank statement
+ level: string;
+
+ // Bank connection to use. It is a *optional* value that
+ // defaults to the default bank connection, if not given.
+ bankConnection: string;
}
.. http:get:: {nexusBase}/bank-accounts/{acctid}/transactions
@@ -265,23 +286,43 @@ to the real bank.
**Request:**
+ This request can accept two formats, depending on whether a
+ new bank connection is being made, or a connection backup is
+ being restored.
+
+
+ This type allows the creation of new bank accounts.
+
+ .. ts:def:: NewBankConnection
+
+ interface NewBankConnection {
+
+ source: string; // only "new" allowed
+
+ // connection name.
+ name: string;
+
+ // type of the connection to make: "ebics" for example.
+ type: string;
+
+ data: BankConnectionNew;
+ }
+
+ This type allows to restore a previously made bank connection.
+
.. ts:def:: BankConnectionRestoreRequest
interface BankConnectionRestoreRequest {
- source: "new" | "backup";
-
+ source: "backup";
+
+ // connection name.
name: string;
- type: string; // "ebics" for example.
- // Restore a previous connection. Take precedence
- // over the 'new' field.
- backup?: BankConnectionBackup;
+
+ // Backup data, as typically returned by the "../export-backup" API.
+ backup: BankConnectionBackup;
passphrase?: string;
-
- // Data to create a fresh bank connection without
- // restoring any backup.
- data?: BankConnectionNew;
}
@@ -307,13 +348,12 @@ to the real bank.
interface BankConnectionBackup {
// The information needed in this type depend entirely
- // on which connectionis being restored.
-
+ // on which connection is being restored.
}
**Response:**
- :status 409 Conflict: The ``name`` field exists already for
+ :http:statuscode:`409 Conflict`: The ``name`` field exists already for
the requesting user.
.. http:post:: {nexusBase}/bank-connections/delete-connection
@@ -331,6 +371,20 @@ to the real bank.
List available bank connections.
+ **Response**
+
+ .. ts:def:: BankConnection
+
+ interface BankConnection {
+
+ // connection type. For example "ebics".
+ type: string;
+
+ // connection name as given by the user at
+ // the moment of creation.
+ name: string;
+ }
+
.. http:get:: {nexusBase}/bank-connections/{connId}
diff --git a/libeufin/ebics.rst b/libeufin/ebics.rst
index 2840a1cd..9960fe1c 100644
--- a/libeufin/ebics.rst
+++ b/libeufin/ebics.rst
@@ -78,7 +78,7 @@ EBICS Glossary
This concept is similar to Taler's merchant backend instance identifiers.
Order Number
- Interchangably called "Order ID".
+ Interchangeably called "Order ID".
Each upload transaction gets a unique order number assigned by the bank server.
The Order Number is used to match VEUs in a second upload to the original order.
@@ -155,7 +155,7 @@ EBICS Glossary
Distributed Electronic Signature (from German "Verteilte Elektronische Unterschrift").
V001
- FTAM encryption algorithm ("Verschlüsselung"), superseeded in EBICS by E002.
+ FTAM encryption algorithm ("Verschlüsselung"), superseded in EBICS by E002.
E002
EBICS encryption process, used to encrypt the order payload.
@@ -329,19 +329,19 @@ The following order types are, for now, not relevant for LibEuFin:
Type: Upload.
Change of the bank-technical key (``K_SIG``).
- Superseeded by HSA.
+ Superseded by HSA.
HCA
Type: Upload.
Change the identification and authentication key as well as the encryption key (``K_IA`` and ``K_ENC``).
- Superseeded by HCS.
+ Superseded by HCS.
PTK
Type: Download.
Download a human-readable protocol of operations done via EBICS.
- Mandatory for German banks. Superseeded by the machine-readable
+ Mandatory for German banks. Superseded by the machine-readable
HAC order type.
diff --git a/libeufin/iso20022.rst b/libeufin/iso20022.rst
index 06d929c2..d96469d0 100644
--- a/libeufin/iso20022.rst
+++ b/libeufin/iso20022.rst
@@ -30,7 +30,7 @@ them (pain.001), it does not use ISO 20022 in its API and internal data model.
Reasons for not using ISO 20022 directly are:
-1. Impedence mismatch. ISO 20022 messages do not map well to query/response
+1. Impedance mismatch. ISO 20022 messages do not map well to query/response
APIs.
2. Cumbersome to use. Even when ISO 20022 messages are directly mapped
to JSON, they are difficult to use due to their verbosity.
diff --git a/manpages/taler-merchant-setup-reserve.1.rst b/manpages/taler-merchant-setup-reserve.1.rst
index 67e1ecb0..594cc164 100644
--- a/manpages/taler-merchant-setup-reserve.1.rst
+++ b/manpages/taler-merchant-setup-reserve.1.rst
@@ -39,7 +39,7 @@ Options
-e URL, --exchange-url=URL
Use URL for the exchange base URL. This is the exchange where
the reserve will be created. The currency used in the amount
- specificiation must be offered by this exchange. Mandatory.
+ specification must be offered by this exchange. Mandatory.
-k KEYFILE, --key=KEYFILE
The specified KEYFILE contains a TLS client private key to be used to authenticate the client. Optional. See also "-p" and "-C".
diff --git a/merchant-benchmark.conf b/merchant-benchmark.conf
new file mode 100644
index 00000000..3702302d
--- /dev/null
+++ b/merchant-benchmark.conf
@@ -0,0 +1,123 @@
+[PATHS]
+# Persistent data storage for the benchmark
+TALER_TEST_HOME = benchmark_home/
+
+[taler]
+# If you change the currency here, you MUST change it
+# throughout the file.
+CURRENCY = EUR
+CURRENCY_ROUND_UNIT = EUR:0.01
+
+[merchant]
+SERVE = tcp
+PORT = 8080
+DB = postgres
+
+[merchantdb-postgres]
+CONFIG = postgres:///talercheck
+
+[exchange]
+DB = postgres
+SERVE = tcp
+PORT = 8081
+BASE_URL = http://localhost:8081/
+MASTER_PUBLIC_KEY = T1VVFQZZARQ1CMF4BN58EE7SKTW5AV2BS18S87ZEGYS4S29J6DNG
+
+[exchangedb-postgres]
+CONFIG = postgres:///talercheck
+
+[auditor]
+DB = postgres
+SERVE = tcp
+PORT = 8083
+BASE_URL = http://the.auditor/
+
+[auditordb-postgres]
+CONFIG = postgres:///talercheck
+
+[bank]
+DATABASE = postgres:///talerbank
+SERVE = http
+HTTP_PORT = 8082
+MAX_DEBT = EUR:5000.0
+MAX_DEBT_BANK = EUR:0.0
+
+[merchant-exchange-test]
+MASTER_KEY = T1VVFQZZARQ1CMF4BN58EE7SKTW5AV2BS18S87ZEGYS4S29J6DNG
+EXCHANGE_BASE_URL = http://localhost:8081/
+CURRENCY = EUR
+
+[exchange-account-exchange]
+# The account name MUST be 'Exchange'
+PAYTO_URI = payto://x-taler-bank/localhost/Exchange
+WIRE_RESPONSE = ${TALER_CONFIG_HOME}/exchange/account.json
+WIRE_GATEWAY_URL = http://localhost:8082/taler-wire-gateway/Exchange/
+WIRE_GATEWAY_AUTH_METHOD = basic
+USERNAME = Exchange
+# The password MUST be 'x'
+PASSWORD = x
+ENABLE_DEBIT = YES
+ENABLE_CREDIT = YES
+
+[fees-x-taler-bank]
+WIRE-FEE-2020 = EUR:0.01
+WIRE-FEE-2021 = EUR:0.01
+WIRE-FEE-2022 = EUR:0.01
+WIRE-FEE-2023 = EUR:0.01
+WIRE-FEE-2024 = EUR:0.01
+WIRE-FEE-2025 = EUR:0.01
+WIRE-FEE-2026 = EUR:0.01
+WIRE-FEE-2027 = EUR:0.01
+CLOSING-FEE-2020 = EUR:0.01
+CLOSING-FEE-2021 = EUR:0.01
+CLOSING-FEE-2022 = EUR:0.01
+CLOSING-FEE-2023 = EUR:0.01
+CLOSING-FEE-2024 = EUR:0.01
+CLOSING-FEE-2025 = EUR:0.01
+CLOSING-FEE-2026 = EUR:0.01
+CLOSING-FEE-2027 = EUR:0.01
+
+[coin_eur_ct_1]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_ct_10]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_1]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_5]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
diff --git a/taler-auditor-manual.rst b/taler-auditor-manual.rst
index 1d84d45d..35f5a799 100644
--- a/taler-auditor-manual.rst
+++ b/taler-auditor-manual.rst
@@ -127,7 +127,7 @@ components:
fail to be imported due to constraint violations, this is an immediate serious
concern that must be addressed manually. The software only verifies the content
of a well-formed exchange database (well-formed with respect to SQL).
- For now, the GNU Taler reference implemenation
+ For now, the GNU Taler reference implementation
only supports Postgres, but the code could be easily extended to
support another DBMS.
@@ -387,7 +387,7 @@ the exchange operator obtains a *blob* with the data about denomination keys
that the exchange operator needs to get signed by every auditor the exchange
wishes (or is forced to) work with.
-In a normal scenario, an auditor must have some secure business proces to
+In a normal scenario, an auditor must have some secure business process to
receive the blob to sign (Website, manual delivery, ...). Note that the
blob does not contain confidential data, but signing the wrong keys would
be fatal. Given the blob, the auditor would sign it using:
@@ -596,7 +596,7 @@ Auditor implementation guide
The auditor implementation is split into five main processes, called
``taler-helper-auditor-XXX``. The split was done to realize the principle of
-least priviledge and to enable independent logic to be possibly run in
+least privilege and to enable independent logic to be possibly run in
parallel. Only the taler-wire-auditor must have (read-only) access to the
exchange's bank account, the other components only need access to the
database.
diff --git a/taler-bank-manual.rst b/taler-bank-manual.rst
index 16221441..6e282ba7 100644
--- a/taler-bank-manual.rst
+++ b/taler-bank-manual.rst
@@ -44,15 +44,14 @@ be switched off during a production deployment.
**Response**
- :status 200 OK:
+ :http:statuscode:`200 OK`:
The new user has been correctly registered.
- :status 409 Conflict:
+ :http:statuscode:`409 Conflict`:
The username requested by the client is not available anymore.
- :status 400 Bad Request:
-
- * Unacceptable characters were given for the username. See
- https://docs.djangoproject.com/en/2.2/ref/contrib/auth/#django.contrib.auth.models.User.username
- for the accepted character set.
+ :http:statuscode:`400 Bad request`:
+ Unacceptable characters were given for the username. See
+ https://docs.djangoproject.com/en/2.2/ref/contrib/auth/#django.contrib.auth.models.User.username
+ for the accepted character set.
**Details**
diff --git a/taler-exchange-manual.rst b/taler-exchange-manual.rst
index 6e4bae85..b156eb90 100644
--- a/taler-exchange-manual.rst
+++ b/taler-exchange-manual.rst
@@ -914,7 +914,7 @@ TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS purpose.
.. [2]
The current implementation does not make provisions for secret
splitting. Still, the use of a hardware security module (HSM) for
- protecting private keys is adviseable, so please contact the
+ protecting private keys is advisable, so please contact the
developers for HSM integration support.
.. [3]
@@ -937,7 +937,7 @@ and clients, or only launch the parallel clients (``-m``), for example for
distributed testing over a network.
For each *parallel* (``-p``) client, a number of *reserves* (``-r``) is first established by
-**transfering** money from a "user" account (42) to the Exchange's account
+**transferring** money from a "user" account (42) to the Exchange's account
with the respective reserve public key as wire subject. Next, the
client will **withdraw** a *number of coins* (``-n``) from the reserve and
**deposit** them. Additionally, a *fraction* (``-R``) of the dirty coins will then be
diff --git a/taler-merchant-api-tutorial.rst b/taler-merchant-api-tutorial.rst
index 7f371f0c..e7af9a4a 100644
--- a/taler-merchant-api-tutorial.rst
+++ b/taler-merchant-api-tutorial.rst
@@ -388,7 +388,7 @@ are recognized in the JSON request object:
- amount: Amount that should be given to the visitor as a tip.
- instance: Merchant instance that grants the tip (each instance may
- have its own independend tipping funds configured).
+ have its own independent tipping funds configured).
- justification: Description of why the tip was granted. Human-readable
text not exposed to the customer, but used by the Back Office.
@@ -544,14 +544,14 @@ signature (``session_sig``). This signature certifies that the wallet
showed a payment receipt for the respective order in the current
session. cookie
-Session-bound payments are triggerd by passing the ``session_id``
+Session-bound payments are triggered by passing the ``session_id``
parameter to the ``/check-payment`` endpoint. The wallet will then
redirect to the fulfillment page, but include an additional
``session_sig`` parameter. The frontend can query ``/check-payment``
with both the ``session_id`` and the ``session_sig`` to verify that the
signature is correct.
-The last session ID that was successfuly used to prove that the payment
+The last session ID that was successfully used to prove that the payment
receipt is in the user’s wallet is also available as ``last_session_id``
in the response to ``/check-payment``.
diff --git a/taler-merchant-manual.rst b/taler-merchant-manual.rst
index 6d148068..60fe441f 100644
--- a/taler-merchant-manual.rst
+++ b/taler-merchant-manual.rst
@@ -16,7 +16,7 @@ GNU Taler is compatible with anti-money-laundering (AML) and
know-your-customer (KYC) regulation, as well as data protection
regulation (such as GDPR).
-GNU Taler is not yet production-ready, after following this manual you
+GNU Taler is not yet production-ready: after following this manual you
will have a backend that can process payments in “KUDOS”, but not
regular currencies. This is not so much because of limitations in the
backend, but because we are not aware of a Taler exchange operator
@@ -27,14 +27,14 @@ offering regular currencies today.
About this manual
-----------------
-This tutorial targets system administrators who want to install a GNU
+This manual targets system administrators who want to install a GNU
Taler merchant *backend*.
We expect some moderate familiarity with the compilation and
-installation of free software packages. An understanding of cryptography
+installation of Free Software packages. An understanding of cryptography
is not required.
-This first chapter of the tutorial will give a brief overview of the
+This first chapter of the manual will give a brief overview of the
overall Taler architecture, describing the environment in which the
Taler backend operates. The second chapter then explains how to install
the software, including key dependencies. The third chapter will explain
@@ -87,7 +87,7 @@ components:
to process financial transactions with Taler. This manual primarily
describes how to install and configure this backend.
- A DBMS which stores the transaction history for the Taler backend.
- For now, the GNU Taler reference implemenation only supports
+ For now, the GNU Taler reference implementation only supports
Postgres, but the code could be easily extended to support another
DBMS. Please review the Postgres documentation for details on
how to configure the database.
@@ -180,6 +180,17 @@ merchant, where the merchant specifies the specific terms of the order.
After an order is created, it is *claimed* by a wallet. Once an order is
claimed by a specific wallet, only that wallet will be able to pay for this
order, to the exclusion of other wallets even if they see the same order URL.
+Sharing order URLs is explicitly allowed: if a user shares and order URL
+with another user, that other user should be given the opportunity to
+purchase the same product.
+
+To prevent unauthorized wallets from claiming an order, merchants can specify
+that claims require authorization in the form of a *claim token*. This is
+useful in case the order ID is predictable (say because an existing order ID
+scheme from the merchant frontend is used) and at the same time malicious
+actors claiming orders is problematic (say because of limited stocks). The use
+of claim tokens is optional, but if a claim token is used, it must be provided
+to the wallet as part of the order URI.
A wallet may *pay* for a claimed order, at which point the order turns into
a (paid) contract. Orders have an expiration date after which the commercial
@@ -189,12 +200,11 @@ allowing the stock to be sold in other orders.
Once a contract has been paid, the merchant should fulfill the contract. It
is possible for the merchant to *refund* a contract order, for example if the
contract cannot be fulfilled after all. Refunds are only possible after the
-customer paid and before the
-exchange has *wired* the payment to the merchant. Once the funds have been
-wired, refunds are no longer allowed by the Taler exchange. The *wire
-deadline* specifies the latest time by which an exchange must wire the funds,
-while the (earlier) *refund deadline* specifies the earliest time when an
-exchange may wire the funds.
+customer paid and before the exchange has *wired* the payment to the
+merchant. Once the funds have been wired, refunds are no longer allowed by the
+Taler exchange. The *wire deadline* specifies the latest time by which an
+exchange must wire the funds, while the (earlier) *refund deadline* specifies
+the earliest time when an exchange may wire the funds.
Contract information is kept for legal reasons, typically to provide tax
records in case of a tax audit. After the *legal expiration* (by default a
@@ -285,8 +295,6 @@ backend:
- libcurl >= 7.26 (or libgnurl >= 7.26)
-- GNU libmicrohttpd >= 0.9.71
-
- libqrencode >= 4.0.0
- GNU libgcrypt >= 1.6
@@ -301,13 +309,16 @@ backend:
- Postgres >= 9.6, including libpq
-- GNUnet (from Git)
+- GNU libmicrohttpd >= 0.9.71
-- GNU Taler exchange (from Git)
+- GNUnet (from Git or see release announcement)
-Except for the last two, these are available in most GNU/Linux
-distributions and should just be installed using the respective package
-manager.
+- GNU Taler exchange (from Git or see release announcement)
+
+Except for the last two, these are available in most GNU/Linux distributions
+and should just be installed using the respective package manager. Be careful
+with GNU libmicrohttpd; here, some distributions only include an older version
+that will not work.
The following sections will provide detailed instructions for installing
the libgnunetutil and GNU Taler exchange dependencies.
@@ -745,7 +756,7 @@ The following is an example for a complete backend configuration:
[merchant-exchange-NAME]
EXCHANGE_BASE_URL = https://exchange.demo.taler.net/
- MASTER_KEY = CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00
+ MASTER_KEY = FH1Y8ZMHCTPQ0YFSZECDH8C9407JR3YN0MF1706PTG24Q4NEWGV0
# If currency does not match [TALER] section, the exchange
# will be ignored!
CURRENCY = KUDOS
@@ -762,7 +773,7 @@ Given the above configuration, the backend will use a database named
The backend will deposit the coins it receives to the exchange at
https://exchange.demo.taler.net/, which has the master key
-"CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00".
+"FH1Y8ZMHCTPQ0YFSZECDH8C9407JR3YN0MF1706PTG24Q4NEWGV0".
Please note that ``doc/config.sh`` will walk you through all
configuration steps, showing how to invoke ``taler-config`` for each of
@@ -813,14 +824,49 @@ Instance setup
Before using the backend, you must at least configure the "default" instance.
-Instances can be configured by POSTing a request to
-:http:post:`/private/instances`. To create a first instance, create a file
-``instance.json`` with an `InstanceConfigurationMessage`
+
+KUDOS Accounts
+--------------
+
+The main configuration data that must be provided for each instance
+is the bank account information.
+
+In order to receive payments, the merchant backend needs to
+communicate bank account details to the exchange.
+
+The bank account information is provided in the form of a ``payto://``-URI.
+See RFC 8905 for the format of ``payto://``-URIs.
+
+For first tests, you should sign up for a KUDOS bank
+account at `https://bank.demo.taler.net/ <https://bank.demo.taler.net/>`_.
+In this case, the payto://-URI will be of the form
+"payto://x-taler-bank/bank.demo.taler.net/$USERNAME" where "$USERNAME"
+must be replaced with the name of the account that was established
+at `https://bank.demo.taler.net/ <https://bank.demo.taler.net/>`_.
+
+
+IBAN Accounts
+-------------
+
+When deploying Taler with the real banking system, you primarily need to
+change the currency of the configuration from KUDOS to the actual currency
+(such as EUR, USD, CHF) and provide a payto://-URI of your real bank
+account. In Europe, this will involve knowing your IBAN number. If you have an
+IBAN, the corresponding payto://-URI is simply "payto://iban/$IBAN" where
+"$IBAN" must be replaced with the actual IBAN number.
+
+
+Setup
+------
+
+With the knowledge of the payto://-URI, instances can be configured by POSTing
+a request to :http:post:`/private/instances`. To create a first instance,
+create a file ``instance.json`` with an `InstanceConfigurationMessage`
::
{
- payto_uris : [ "payto://iban/IBANNUMBERHERE" ],
+ payto_uris : [ "$PAYTO_URI" ],
id : "default",
name: "example.com",
address: { country : "zz" },
@@ -832,6 +878,16 @@ Instances can be configured by POSTing a request to
default_pay_delay: { d_ms : 1209600000 },
}
+In the text above, you must replace "$PAYTO_URI" with your actual
+payto://-URI. Also, be sure to replace KUDOS with the fiat currency if the
+setup is for an actual bank. The "name" field will be shown as the name of
+your shop. The "address" field is expected to contain your shop's physical
+address. The various defaults specify defaults for transaction fees your shop
+is willing to cover, how long offers made to the customer are valid, and how
+long the exchange has before it must wire the funds to your bank
+account. Those defaults can be modified for individual orders.
+For details, see the :ref:`contract terms <contract-terms>` specification.
+
You can then create the instance using:
::
@@ -847,21 +903,6 @@ or purge (deleting all associated data) instances exist as well and are document
in the :ref:`Merchant Backend API documentation <merchant-api>`.
-Accounts
---------
-
-The main configuration data that must be provided for each instance
-is the bank account information.
-
-In order to receive payments, the merchant backend needs to
-communicate bank account details to the exchange.
-
-The bank account information is provided in the form of a ``payto://``-URL.
-
-See RFC XXXX for the format of ``payto://``-URLs.
-
-
-
.. _Secure-setup:
Secure setup
@@ -880,18 +921,30 @@ Using UNIX domain sockets
-------------------------
To ensure that the merchant backend is not exposed directly to the network,
-you should bind the backend to a UNIX domain socket:
+you *should* bind the backend to a UNIX domain socket:
::
$ taler-config -s MERCHANT -o SERVE -V UNIX
$ taler-config -s MERCHANT -o UNIXPATH -V /some/path/here.sock
+Do not use a UNIX domain socket path in "/tmp": systemd (or other init
+systems) may give Web servers a private "/tmp" thereby hiding UNIX domain
+sockets created by other users/processes in "/tmp".
+
+If UNIX domain sockets are for some reason not possible, you *may* use a
+host-based firewall to block access to the TCP port of the merchant backend,
+but this is *not recommended*. Relying on NAT or network firewalls for access
+control is gross negligence.
+
+
Reverse proxy configuration
---------------------------
-Assuming your domain name is /example.com/ and you have TLS configured,
-a possible reverse proxy directive for Nginx would be:
+Nginx
+^^^^^
+
+For Nginx, a possible basic reverse proxy configuration would be:
::
@@ -901,9 +954,39 @@ a possible reverse proxy directive for Nginx would be:
proxy_set_header X-Forwarded-Host "example.com";
proxy_set_header X-Forwarded-Proto "https";
-Leave out the last line if your Nginx reverse proxy does not have HTTPS
-enabled. Make sure to restart the /taler-merchant-httpd/ process after
-changing the ``SERVE`` configuration.
+Note that the above assumes your domain name is /example.com/ and that you
+have TLS configured. Leave out the last line if your Nginx reverse proxy does
+not have HTTPS enabled. Make sure to restart the /taler-merchant-httpd/
+process after changing the ``SERVE`` configuration.
+
+Apache
+^^^^^^
+
+In Apache, make sure you have "mod_proxy", "mod_proxy_http" and
+"mod_headers" enabled:
+
+ ::
+ a2enmod proxy
+ a2enmod proxy_http
+ a2enmod headers
+
+Then configure your Apache reverse proxy like this (you may change the
+endpoint):
+
+ ::
+
+ <Location "/">
+ ProxyPass "unix:/some/path/here.sock|http://example.com/"
+ RequestHeader add "X-Forwarded-Proto" "https"
+ </Location>
+
+Note that the above again assumes your domain name is /example.com/ and that
+you have TLS configured. Note that you must add the "https" header unless
+your site is not available via TLS.
+
+The above configuration(s) are both incomplete. You must still additionally
+setup access control!
+
Access control
--------------
@@ -922,6 +1005,119 @@ Note that all of the other endpoints (without /private/) are expected to be
fully exposed to the Internet, and wallets may have to interact with those
endpoints directly without client authentication.
+Nginx
+^^^^^
+
+For Nginx, you can implement token-based merchant backend authentication as
+follows:
+
+ ::
+
+ location ~ /private/ {
+ if ($http_authorization !~ "(?i)ApiKey SECURITYTOKEN") {
+ return 401;
+ }
+ proxy_pass ...; // as above
+ }
+
+Here, "SECURITYTOKEN" should be replaced with the actual shared secret. Note
+that the "~" ensures that the above matches all endpoints that include the
+string "/private/. If you only run a single instance, you could simply
+specify "/private/" without the "~" to only configure the access policy for
+the default instance.
+
+If you are running different instances on the same backend, you
+likely will want to specify different access control tokens for
+each instance:
+
+ ::
+
+ location ~ ^/instances/foo/private/ {
+ if ($http_authorization !~ "(?i)ApiKey FOOTOKEN") {
+ return 401;
+ }
+ proxy_pass ...; // as above
+ }
+ location ~ ^/instances/bar/private/ {
+ if ($http_authorization !~ "(?i)ApiKey BARTOKEN") {
+ return 401;
+ }
+ proxy_pass ...; // as above
+ }
+ location /private/ {
+ if ($http_authorization !~ "(?i)ApiKey MASTERTOKEN") {
+ return 401;
+ }
+ proxy_pass ...; // as above
+ }
+ location ~ /private/ {
+ return 401; // access to instances not explicitly configured is forbidden
+ }
+
+Apache
+^^^^^^
+
+For Apache, you should first enable "mod_rewrite":
+
+ ::
+ a2enmod rewrite
+
+Then, you can restrict to an access control token using:
+
+ ::
+ <Location "/">
+ RewriteEngine On
+ RewriteCond "%{HTTP:AUTHORIZATION}" "!=SECURITYTOKEN"
+ RewriteRule "(.+)/private/" "-" [F]
+
+ ProxyPass "unix:/some/path/here.sock|http://example.com/"
+ </Location>
+
+Here, "SECURITYTOKEN" should be replaced with the actual shared secret. Note
+that the "(.+)" ensures that the above matches all endpoints that include the
+string "/private/. If you only run a single instance, you could simply
+specify "/private/" without the "~" to only configure the access policy for
+the default instance.
+
+If you are running different instances on the same backend, you
+likely will want to specify different access control tokens for
+each instance:
+
+ ::
+ <Location "/instances/foo/">
+ RewriteEngine On
+ RewriteCond "%{HTTP:AUTHORIZATION}" "!=FOOTOKEN"
+ RewriteRule "/instances/foo/private/" "-" [F]
+
+ ProxyPass ... # as above
+ </Location>
+
+ <Location "/instances/bar/">
+ RewriteEngine On
+ RewriteCond "%{HTTP:AUTHORIZATION}" "!=BARTOKEN"
+ RewriteRule "/instances/bar/private/" "-" [F]
+
+ ProxyPass ... # as above
+ </Location>
+
+ <Location "/">
+ RewriteEngine On
+ RewriteCond "%{HTTP:AUTHORIZATION}" "!=MASTERTOKEN"
+ RewriteRule "/private/" "-" [F]
+ RewriteRule "(.+)/private/" "-" [F] # reject all others
+
+ ProxyPass ... # as above
+ </Location>
+
+Please note that these are simply examples of how one could use Nginx or
+Apache2 for access control. Both HTTP servers support many other forms of
+authentication, including TLS client certificates, HTTP basic and digest
+authentication and others, which can all be used (possibly in combination) to
+restrict access to the internal API to authorized clients.
+
+System admininistrators are strongly advised to test their access control
+setup before going into production!
+
Customization
=============
@@ -962,7 +1158,9 @@ Limitations
-----------
All of the static files must fit into memory and it must be possible for the
-process to hold open file handles for all of these files.
+process to hold open file handles for all of these files. You may want
+to increase the "ulimit" of the taler-merchant-httpd process if you have
+templates for many languages.
The backend determines the mime type based on the file's extension. The list
of supported extensions is hard-coded and includes common text and image
@@ -1100,7 +1298,7 @@ in a special “402 Payment Required” response inside the ``X-Taler-Tip``
header.
The frontend should handle errors returned by the backend, such as
-missconfigured instances or a lack of remaining funds for tipping.
+misconfigured instances or a lack of remaining funds for tipping.
.. _Picking-up-of-the-tip:
@@ -1123,7 +1321,7 @@ Advanced topics
Database Scheme
---------------
-The merchant database must be initialized using taler-merchant-dbinit.
+The merchant database must be initialized using /taler-merchant-dbinit/.
This tool creates the tables required by the Taler merchant to operate.
The tool also allows you to reset the Taler merchant database, which is
useful for test cases but should never be used in production. Finally,
@@ -1265,78 +1463,91 @@ option.
+Advanced experimental features
+==============================
+
+This section describes features that most merchants will not
+need, or will not need initially.
+
.. _MerchantBenchmarking:
Benchmarking
------------
-.. index:: testing database
-
-NOTE: This section is dated and should be reviewed!
-
-FIXME: which coin denominations are needed for the benchmark?
-
-FIXME: provide "minimum" configuration file!
-
-FIXME: explain Postgres setup!
-
+The merchant codebase offers the ``taler-merchant-benchmark`` tool to
+populate the database with fake payments. This tool is in charge of
+starting a merchant, exchange, and bank processes, and provide them all
+the input to accomplish payments. Note that each component will use its
+own configuration (as they would do in production).
-Setup: create Exchange account and two user accounts ``42`` and ``43`` at
-the bank:
+The main goal of the benchmarking tool is to serve as a starting point (!) for
+merchants that are interested in developing stress tests to see how far their
+infrastructure can scale.
-::
+The current tool has already a few options, but we expect that to deliver
+*relevant* results it will need to be customized to better reflect the
+workload of a particular merchant. This customization would at this point
+likely involve writing (C) code. We welcome contributions to make it easier
+to customize the benchmark and/or to cover more realistic workloads from the
+start.
- $ taler-bank-manage django add_bank_account Exchange
- $ taler-bank-manage django add_bank_account 42
- $ taler-bank-manage django add_bank_account 43
-Setup exchange password using:
+Benchmark setup
+---------------
-::
+The taler-merchant-benchmark tool will automatically launch and configure the
+exchange, (Python) bank and other tools required for the benchmark. However,
+the configuration file must be provided and have consistent options set. The
+options that require special care include the exchange's public key (which
+must match the private key in the file specified by the configuration), the
+currency (which must be consistent across the file), the denomination
+structure (which must enable payments in the range of 100ths of the unit
+currency (often called cents). Furthermore, the benchmark will set the
+Exchange bank account password to be "x", so the configuration must also
+specify "x" for the passphrase. Finally, the bank must be configured to allow
+for substantial debt least the transactions by the benchmark run out of
+digital cash.
- $ taler-bank-manage django changepassword_unsafe Exchange PASSWORD
+A relatively minimal configuration could look like this:
-Configure merchant and exchange, then run:
+.. literalinclude:: merchant-benchmark.conf
-::
- $ taler-exchange-dbinit
- $ taler-exchange-keyup
- $ taler-merchant-dbinit
+Note that the public key must match the exchange's
+private key and that the Postgres database must
+exist before launching the benchmark. You also
+will need to ensure that the Exchange's
+details are setup, usually by running
-Launch bank, exchange and merchant backends:
+ ::
-::
+ taler-exchange-wire -c $CONFIG_FILE
+ taler-exchange-keyup -c $CONFIG_FILE
- $ taler-bank-manage serve-http &
- $ taler-exchange-httpd &
- $ taler-merchant-httpd &
+where "$CONFIG_FILE" should be replaced by
+the configuration file that is to be used.
-The merchant codebase offers the ``taler-merchant-benchmark`` tool to
-populate the database with fake payments. This tool is in charge of
-starting a merchant, exchange, and bank processes, and provide them all
-the input to accomplish payments. Note that each component will use its
-own configuration (as they would do in production).
+Running the benchmark command
+-----------------------------
The tool takes all of the values it needs from the command line, with
-some of them being mandatory. Among those, we have:
-
-- ``--bank-url=URL`` Assume that the bank is serving under the base URL
- *URL*. This option is only actually used by the tool to check if the
- bank was well launched.
+one of them being mandatory:
-- ``--merchant-url=URL`` Reach the merchant through *URL*, for
- downloading contracts and sending payments.
+- ``--exchange-account=SECTION`` Specifies which configuration
+ section specifies the bank account for the exchange that
+ should be used for the benchmark. For the example
+ configuration above, the SECTION value provided must be
+ "exchange-account-exchange".
-The tool then comes with two operation modes: *ordinary*, and *corner*.
+The tool comes with two operation modes: *ordinary*, and *corner*.
The first just executes normal payments, meaning that it uses the
default instance and make sure that all payments get aggregated. The
second gives the chance to leave some payments unaggregated, and also to
use merchant instances other than the default (which is, actually, the
one used by default by the tool).
-Note: the abilty of driving the aggregation policy is useful for testing
+Note: the ability of driving the aggregation policy is useful for testing
the backoffice facility.
Any subcommand is also equipped with the canonical ``--help`` option, so
@@ -1357,10 +1568,6 @@ interesting, there are:
- ``--unaggregated-number=UN`` This option instructs the tool to
perform *UN* (one coin) payments that will be left unaggregated.
-- ``--alt-instance=AI`` This option instructs the tool to perform
- payments using the merchant instance *AI* (instead of the *default*
- instance)
-
As for the ``ordinary`` subcommand, it is worth explaining the following
options:
@@ -1374,135 +1581,15 @@ options:
actual measurement of performance is provided (despite of the
’benchmark’ work used in the tool’s name).
-.. [1]
- https://docs.docker.com/
-
-
-
-Diagnostics
-===========
-
-This chapter includes various (very unpolished) sections on specific
-topics that might be helpful to understand how the exchange operates,
-which files should be backed up. The information may also be helpful for
-diagnostics.
-
-This chapter contains some legacy documentation we need to update
-before it can be considered even reasonably accurate.
-
-
-Taler payments generator
-------------------------
-
-This tool does not exist anymore right now...
-
-The tool ``taler-merchant-generate-payments`` can be used to test the
-merchant backend installation. It implements all the payment’s steps in
-a programmatically way, relying on the backend you give it as input.
-Note that this tool gets installed along all the merchant backend’s
-binaries.
-
-This tool gets configured by a config file, that must have the following
-layout:
-
-::
-
- [PAYMENTS-GENERATOR]
-
- # The exchange used during the test: make sure the merchant backend
- # being tested accpets this exchange.
- # If the sysadmin wants, she can also install a local exchange
- # and test against it.
- EXCHANGE = https://exchange.demo.taler.net/
-
- # This value must indicate some URL where the backend
- # to be tested is listening; it doesn't have to be the
- # "official" one, though.
- MERCHANT = http://localbackend/
-
- # This value is used when the tool tries to withdraw coins,
- # and must match the bank used by the exchange. If the test is
- # done against the exchange at https://exchange.demo.taler.net/,
- # then this value can be "https://bank.demo.taler.net/".
- BANK = https://bank.demo.taler.net/
-
- # The merchant instance in charge of serving the payment.
- # Make sure this instance has a bank account at the same bank
- # indicated by the 'bank' option above.
- INSTANCE = default
-
- # The currency used during the test. Must match the one used
- # by merchant backend and exchange.
- CURRENCY = KUDOS
-
-Run the test in the following way:
-
-::
-
- $ taler-merchant-generate-payments [-c config] [-e EURL] [-m MURL]
-
-The argument ``config`` given to ``-c`` points to the configuration file
-and is optional – ``^/.config/taler.conf`` will be checked by default.
-By default, the tool forks two processes: one for the merchant backend,
-and one for the exchange. The option ``-e`` (``-m``) avoids any exchange
-(merchant backend) fork, and just runs the generator against the
-exchange (merchant backend) running at ``EURL`` (``MURL``).
-
-Please NOTE that the generator contains *hardcoded* values, as for
-deposit fees of the coins it uses. In order to work against the used
-exchange, those values MUST match the ones used by the exchange.
-
-The following example shows how the generator "sets" a deposit fee of
-EUR:0.01 for the 5 EURO coin.
-
-::
-
- // from <merchant_repository>/src/sample/generate_payments.c
- { .oc = OC_PAY,
- .label = "deposit-simple",
- .expected_response_code = MHD_HTTP_OK,
- .details.pay.contract_ref = "create-proposal-1",
- .details.pay.coin_ref = "withdraw-coin-1",
- .details.pay.amount_with_fee = concat_amount (currency, "5"),
- .details.pay.amount_without_fee = concat_amount (currency, "4.99") },
-
-The logic calculates the deposit fee according to the subtraction:
-``amount_with_fee - amount_without_fee``.
-
-The following example shows a 5 EURO coin configuration - needed by the
-used exchange - which is compatible with the hardcoded example above.
-
-::
-
- [COIN_eur_5]
- value = EUR:5
- duration_overlap = 5 minutes
- duration_withdraw = 7 days
- duration_spend = 2 years
- duration_legal = 3 years
- fee_withdraw = EUR:0.00
- fee_deposit = EUR:0.01 # important bit
- fee_refresh = EUR:0.00
- fee_refund = EUR:0.00
- rsa_keysize = 1024
-
-If the command terminates with no errors, then the merchant backend is
-correctly installed.
-
-After this operation is done, the merchant database will have some dummy
-data in it, so it may be convenient to clean all the tables; to this
-purpose, issue the following command:
-
-::
-
- $ taler-merchant-dbinit -r
+Temporarily Abandoned Features
+==============================
+.. [1]
+ https://docs.docker.com/
-Legacy
-======
Installing Taler using Docker
-----------------------------
diff --git a/taler-nfc-guide.rst b/taler-nfc-guide.rst
index 88fa13d5..81433878 100644
--- a/taler-nfc-guide.rst
+++ b/taler-nfc-guide.rst
@@ -241,7 +241,7 @@ NFC. In particular, only JSON request and response bodies are allowed.
It is currently assumed that the requests and responses fit into one APDU frame.
For devices with more limited maximum APDU sizes, additional TIDs for segmented
-tunnel requests/responsed may be defined in the future.
+tunnel requests/responses may be defined in the future.
A request for tunneling is initiated with TID ``0x03`` and responded to with
TID ``0x02`` (see tables above). A tunneling request is identified by a