summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/type-extractor/.gitignore1
-rw-r--r--contrib/type-extractor/extract-types.mjs272
-rw-r--r--contrib/type-extractor/package.json15
-rw-r--r--contrib/type-extractor/pnpm-lock.yaml24
-rw-r--r--core/api-auditor.rst2441
-rw-r--r--core/api-bank-conversion-info.rst4
-rw-r--r--core/api-bank-integration.rst6
-rw-r--r--core/api-bank-wire.rst2
-rw-r--r--core/api-challenger.rst2
-rw-r--r--core/api-common.rst58
-rw-r--r--core/api-corebank.rst76
-rw-r--r--core/api-donau.rst234
-rw-r--r--core/api-exchange.rst970
-rw-r--r--core/api-mailbox.rst20
-rw-r--r--core/api-merchant.rst274
-rw-r--r--core/api-terminal.rst5
-rw-r--r--design-documents/023-taler-kyc.rst632
-rw-r--r--design-documents/037-wallet-transactions-lifecycle.rst11
-rw-r--r--design-documents/046-mumimo-contracts.rst2
-rw-r--r--design-documents/053-wallet-ui.rst20
-rw-r--r--design-documents/054-dynamic-form.rst673
-rw-r--r--extract-tsdefs/extract.ts35
-rw-r--r--images/regional-arch.pngbin117525 -> 0 bytes
-rw-r--r--images/transaction-withdrawal-states.dot3
-rw-r--r--index.rst2
-rw-r--r--libeufin/index.rst1
-rw-r--r--libeufin/regional-automated-manual.rst1
-rw-r--r--libeufin/regional-custom-manual.rst1
-rw-r--r--libeufin/regional-manual-use.rst (renamed from frags/regional-manual-use.rst)19
-rw-r--r--manpages/libeufin-nexus.1.rst22
-rw-r--r--manpages/libeufin-nexus.conf.5.rst54
-rw-r--r--manpages/taler.conf.5.rst196
-rw-r--r--orphaned/python-guidelines.rst (renamed from python-guidelines.rst)0
-rw-r--r--orphaned/qa-0.9.4.rst (renamed from checklists/qa-0.9.4.rst)0
-rw-r--r--screenshots/cta-balance-list-android-1.pngbin0 -> 51463 bytes
l---------screenshots/cta-balance-list-android-latest.png2
-rw-r--r--screenshots/cta-payment-paid-android-1.pngbin0 -> 44605 bytes
l---------screenshots/cta-payment-paid-android-latest.png2
-rw-r--r--screenshots/cta-peer-pull-initiate-android-1.pngbin0 -> 43717 bytes
l---------screenshots/cta-peer-pull-initiate-android-latest.png2
-rw-r--r--screenshots/cta-transaction-list-android-1.pngbin0 -> 86414 bytes
l---------screenshots/cta-transaction-list-android-latest.png2
-rw-r--r--screenshots/cta-url-entry-android-1.pngbin0 -> 23514 bytes
l---------screenshots/cta-url-entry-android-latest.png2
-rw-r--r--screenshots/cta-wire-transfer-android-1.pngbin0 -> 116541 bytes
l---------screenshots/cta-wire-transfer-android-latest.png2
-rw-r--r--screenshots/cta-withdraw-done-android-1.pngbin0 -> 33779 bytes
l---------screenshots/cta-withdraw-done-android-latest.png2
-rw-r--r--screenshots/dynamic-forms.amount.pngbin0 -> 9763 bytes
-rw-r--r--screenshots/dynamic-forms.boolean.pngbin0 -> 7492 bytes
-rw-r--r--screenshots/dynamic-forms.choice.pngbin0 -> 9528 bytes
-rw-r--r--screenshots/dynamic-forms.decorative-elements.pngbin0 -> 22289 bytes
-rw-r--r--screenshots/dynamic-forms.file.pngbin0 -> 51502 bytes
-rw-r--r--screenshots/dynamic-forms.list.pngbin0 -> 17007 bytes
-rw-r--r--screenshots/dynamic-forms.time.pngbin0 -> 10026 bytes
-rw-r--r--system-administration/borgbackup-tutorial.rst172
-rw-r--r--system-administration/images/borg-logo.pngbin0 -> 2829 bytes
-rw-r--r--system-administration/images/grafana-postgres-exporter.png (renamed from images/grafana-postgres-exporter.png)bin244971 -> 244971 bytes
-rw-r--r--system-administration/images/kuma.png (renamed from images/kuma.png)bin244687 -> 244687 bytes
-rw-r--r--system-administration/images/lego-logo.svg1
-rw-r--r--system-administration/images/taler-monitoring-infrastructure.png (renamed from images/taler-monitoring-infrastructure.png)bin85006 -> 85006 bytes
-rw-r--r--system-administration/images/uptime-kuma-edit.png (renamed from images/uptime-kuma-edit.png)bin116550 -> 116550 bytes
-rw-r--r--system-administration/images/uptime-kuma-from-grafana.png (renamed from images/uptime-kuma-from-grafana.png)bin345702 -> 345702 bytes
-rw-r--r--system-administration/index.rst27
-rw-r--r--system-administration/lego-certificates.rst144
-rw-r--r--system-administration/taler-monitoring-infrastructure.rst (renamed from taler-monitoring-infrastructure.rst)29
-rw-r--r--taler-developer-manual.rst8
-rw-r--r--taler-exchange-manual.rst183
-rw-r--r--taler-merchant-manual.rst8
-rw-r--r--taler-merchant-pos-terminal.rst13
-rw-r--r--wallet/wallet-core.md365
71 files changed, 5693 insertions, 1347 deletions
diff --git a/contrib/type-extractor/.gitignore b/contrib/type-extractor/.gitignore
new file mode 100644
index 00000000..b512c09d
--- /dev/null
+++ b/contrib/type-extractor/.gitignore
@@ -0,0 +1 @@
+node_modules \ No newline at end of file
diff --git a/contrib/type-extractor/extract-types.mjs b/contrib/type-extractor/extract-types.mjs
new file mode 100644
index 00000000..86bccd9c
--- /dev/null
+++ b/contrib/type-extractor/extract-types.mjs
@@ -0,0 +1,272 @@
+import fsSync, { promises as fs } from "fs";
+import ts from "typescript";
+import * as path from "path";
+
+const ignoredExports = ["PublishedAgeRestrictionBaseKey"];
+
+/**
+ * @param {string} file
+ */
+const runFileJob = async (file) => {
+ // TODO: idk why this was async, im sure i had a reason
+ let workingFile = file;
+ const tsDefs = file.match(/[\t ]*\.\. ts\:def\:\: [a-zA-Z][a-zA-Z0-9_]*/g);
+ /** @type string[] */
+ const defines = [];
+ let dtsOutput = "";
+ if (tsDefs)
+ for (const def of tsDefs) {
+ if (!def) {
+ console.warn("No matches in ", file);
+ break;
+ }
+ workingFile = workingFile.substring(workingFile.indexOf(def));
+ let [defMatch, indentation, defName] = def.match(
+ /([\t ])*\.\. ts\:def\:\: ([a-zA-Z][a-zA-Z0-9_]*) *\n?/
+ );
+
+ if (ignoredExports.includes(defName)) continue;
+
+ // Extract the ts def
+ indentation = indentation ?? "";
+ workingFile = workingFile.substring(defMatch.length);
+ const workingFileLines = workingFile.split("\n");
+ let tsMatch = "";
+ while (workingFileLines[0]?.trim() === "") workingFileLines.shift();
+ while (
+ (workingFileLines[0]?.trim() === "" ||
+ (workingFileLines[0] &&
+ new RegExp("^" + "[ \\t]".repeat(indentation.length + 2)).test(
+ workingFileLines[0]
+ ))) &&
+ !workingFileLines[0]?.trim()?.startsWith(".. ts:def::")
+ ) {
+ if (workingFileLines[0].length > indentation.length + 2)
+ workingFileLines[0] = workingFileLines[0].substring(
+ indentation.length + 2
+ );
+ tsMatch += workingFileLines.shift() + "\n";
+ }
+ workingFile = workingFileLines.join("\n");
+
+ // Convert comments to JSDocs
+ tsMatch = tsMatch
+ .replace(/([ \t]*\/\/.*\n?)+/g, (match) => {
+ match = match
+ .split("\n")
+ .map((v) => v.replace(/[ \t]+\/\/ ?/, "").trim())
+ .join("\n")
+ .trim();
+ if (match.includes("\n"))
+ match = `/**
+${match
+ .split("\n")
+ .map((v) => (v.trimStart().startsWith("//") ? v.replace("//", "") : v))
+ .map((v) => " *" + (v.startsWith(" ") ? "" : " ") + v)
+ .join("\n")
+ .replace(/\*\//g, "*​/")}
+ */
+`;
+ else
+ match = `/**
+ * ${(match.trimStart().startsWith("//") ? match.replace("//", "") : match)
+ .trim()
+ .replace(/\*\//g, "*​/")}
+ */
+`;
+ return match;
+ })
+ .trim();
+
+ defines.push(defName);
+ dtsOutput += tsMatch + "\n";
+ }
+
+ if (defines.length === 0) return null; // nothing to give back, just exit
+
+ // Now, find the unknown imports
+
+ dtsOutput += `
+export { ${defines.join(", ")} };
+`;
+
+ // Job is done, return
+ return {
+ defines,
+ dtsOutput,
+ };
+};
+
+(async () => {
+ const genDocsForDirs = ["core/"].map((v) => path.resolve(process.argv[2], v));
+ const genDocsForFiles = (
+ await Promise.all(
+ genDocsForDirs.map(async (dir) =>
+ (await fs.readdir(dir)).map((file) => path.join(dir, file))
+ )
+ )
+ ).flat();
+ const output = path.resolve(
+ process.env.TYPE_OUTPUT ?? process.env.TMP ?? process.env.TEMP ?? "/tmp",
+ "net.taler.docs.ts-extracted"
+ );
+ const tsDocOutput = path.join(output, "dts");
+ // const zodOutput = path.join(output, "zod"); // TODO: this would be cool to have in future
+
+ if (fsSync.existsSync(tsDocOutput))
+ await fs.rm(tsDocOutput, { recursive: true });
+ await fs.mkdir(tsDocOutput, {
+ recursive: true,
+ });
+ // if (fsSync.existsSync(zodOutput)) await fs.rm(zodOutput, { recursive: true });
+ // await fs.mkdir(zodOutput, {
+ // recursive: true,
+ // });
+ const jobResults = (
+ await Promise.all(
+ genDocsForFiles.map(async (filepath) => ({
+ source: filepath,
+ output: path.join(
+ tsDocOutput,
+ path.basename(filepath).replace(".rst", ".ts")
+ ),
+ result: await runFileJob(await fs.readFile(filepath, "utf-8")),
+ }))
+ )
+ ).filter((v) => v.result !== null);
+ // Polyfilling!!!
+ // TODO: Extract these to standalone .rst files!
+ jobResults.push({
+ source: "/tmp/net.taler.docs.extracted/_forced_polyfill",
+ output: path.join(tsDocOutput, "post-polyfill.ts"),
+ // This polyfill overwrites any object defined elsewhere
+ result: await runFileJob(`
+.. ts:def:: Integer
+ // An integer value.
+ // @integer
+ type Integer = number;
+`),
+ });
+ jobResults.unshift({
+ source: "/tmp/net.taler.docs.extracted/_polyfill",
+ output: path.join(tsDocOutput, "polyfill.ts"),
+ // This polyfill can be overwritten by the actual docs; it's contents will be outputted but ignored by the import resolver if overwritten
+ result: await runFileJob(`
+.. ts:def:: PaytoHash
+ // A Binary Object
+ type PaytoHash = string;
+.. ts:def:: AgeCommitmentHash
+ // A Binary Object
+ type AgeCommitmentHash = string;
+.. ts:def:: TALER_RefreshCommitmentP
+ // A Binary Object
+ type TALER_RefreshCommitmentP = string;
+.. ts:def:: WireTransferIdentifierRawP
+ // A Binary Object
+ type WireTransferIdentifierRawP = string;
+.. ts:def:: Base32
+ // Binary data is generally encoded using Crockford’s variant of Base32 (https://www.crockford.com/wrmg/base32.html), except that “U” is not excluded but also decodes to “V” to make OCR easy. We will still simply use the JSON type “base32” and the term “Crockford Base32” in the text to refer to the resulting encoding.
+ type Base32 = string;
+.. ts:def:: ExtensionManifest
+ // Mostly undocumented object; see {@link https://docs.taler.net/design-documents/006-extensions.html#extensionmanifest-object} for what it likely is?
+ interface ExtensionManifest {
+ // The criticality of the extension MUST be provided. It has the same
+ // semantics as "critical" has for extensions in X.509:
+ // - if "true", the client must "understand" the extension before
+ // proceeding,
+ // - if "false", clients can safely skip extensions they do not
+ // understand.
+ // (see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)
+ critical: boolean;
+
+ // The version information MUST be provided in Taler's protocol version
+ // ranges notation, see
+ // https://docs.taler.net/core/api-common.html#protocol-version-ranges
+ version: LibtoolVersion;
+
+ // Optional configuration object, defined by the feature itself
+ config?: object;
+ }
+.. ts:def:: WadId
+ // https://docs.taler.net/core/api-common.html#wadid
+ type WadId = string;
+.. ts:def:: ContractChoice
+ // Untyped in documentation https://docs.taler.net/design-documents/046-mumimo-contracts.html#tsref-type-ContractChoice
+ type ContractChoice = any;
+`),
+ });
+ // Resolve Inputs
+ /** @type {Record<string,string>} */
+ const fileByExport = {};
+ jobResults.forEach((result) => {
+ // these are processed intentionally in-order; the last items in jobResults will take priority over the first; polyfill will always take peak priority
+ result.result.defines.forEach(
+ (define) => (fileByExport[define] = result.output)
+ );
+ });
+ await Promise.all(
+ jobResults.map((result) => {
+ // now that the table is populated, lets resolve imports
+ const src = result.result.dtsOutput;
+
+ /** @type {string[]} */
+ const toBeImported = [];
+
+ const sourceFile = ts.createSourceFile(path.basename(result.output), src);
+
+ /**
+ * @param {ts.Node} node
+ */
+ const astWalker = (node) => {
+ if (node.kind === ts.SyntaxKind.TypeReference) {
+ /** @type {ts.TypeReferenceNode} */
+ const typeRefNode = node;
+ const identifier = typeRefNode.typeName.escapedText;
+ if (!result.result.defines.includes(identifier))
+ toBeImported.push(identifier);
+ }
+ ts.forEachChild(node, astWalker);
+ };
+ astWalker(sourceFile);
+ result.result.dtsOutput = `${toBeImported
+ .filter((v, i, a) => a.indexOf(v) === i)
+ .map((v) => {
+ if (fileByExport[v])
+ return `import { ${v} } from ${JSON.stringify(
+ "./" + path.basename(fileByExport[v])
+ )}`;
+ console.warn("Could not find reference to", v);
+ return "// WARN: UNKNOWN REF: " + JSON.stringify(v);
+ })
+ .join("\n")}
+${result.result.dtsOutput}`;
+ })
+ );
+ // Write outputs
+ await Promise.all(
+ jobResults.map(async ({ output, result }) => {
+ await fs.writeFile(output, result.dtsOutput);
+ })
+ );
+ // Write the index.ts file
+ /** @type {Record<string,string[]>} */
+ const exportsByFile = {};
+ for (const [exported, file] of Object.entries(fileByExport)) {
+ exportsByFile[file] = exportsByFile[file] ?? [];
+ exportsByFile[file].push(exported);
+ }
+ await fs.writeFile(
+ path.join(tsDocOutput, "main.ts"),
+ Object.entries(exportsByFile)
+ .map(
+ ([file, exports]) =>
+ // We could use export * from, but then we'd get class conflicts if 2 separate files declare the same type - including if our polyfill overwrites or gets overwritten
+ `export { ${exports.join(", ")} } from ${JSON.stringify(
+ "./" + path.basename(file) // TODO: use path.relative
+ )};`
+ )
+ .join("")
+ );
+
+ // TODO: call tsc on all our stuff, ensure it validates
+})();
diff --git a/contrib/type-extractor/package.json b/contrib/type-extractor/package.json
new file mode 100644
index 00000000..4fbf0020
--- /dev/null
+++ b/contrib/type-extractor/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "type-extractor",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "typescript": "^5.4.5"
+ }
+}
diff --git a/contrib/type-extractor/pnpm-lock.yaml b/contrib/type-extractor/pnpm-lock.yaml
new file mode 100644
index 00000000..76630473
--- /dev/null
+++ b/contrib/type-extractor/pnpm-lock.yaml
@@ -0,0 +1,24 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ typescript:
+ specifier: ^5.4.5
+ version: 5.4.5
+
+packages:
+
+ typescript@5.4.5:
+ resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+snapshots:
+
+ typescript@5.4.5: {}
diff --git a/core/api-auditor.rst b/core/api-auditor.rst
index 61e448b8..d409685d 100644
--- a/core/api-auditor.rst
+++ b/core/api-auditor.rst
@@ -97,58 +97,6 @@ know-your-customer (KYC) registration before issuing contracts.
time of this writing).
-.. _exchange_signkeys-list:
-
-------------------------------------
-Obtaining Exchange Signing Keys List
-------------------------------------
-
-This API is used by auditor to obtain a list of all exchanges signing keys to be audited.
-
-.. http:get:: /exchange-sign-keys
-
- Get a list of all exchange signing keys to be audited by the auditor.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`ExchangeSignKeysList` object. This request should
- virtually always be successful.
-
- **Details:**
-
- .. ts:def:: ExchangeSignKeysList
-
- interface ExchangeSignKeysList {
- // Exchange signing keys to be audited by the auditor.
- exchange-sign-key: ExchangeSignKeyEntry[];
- }
-
- .. ts:def:: ExchangeSignKeyEntry
-
- interface ExchangeSignKeyEntry {
-
- // Public online signing key of the exchange.
- exchange_pub: EddsaPublicKey;
-
- // Base URL of the exchange.
- master_sig: EddsaSignature;
-
- // Time when online signing key will first be use.
- ep_valid_from: Timestamp;
-
- // Time when this online signing key will no longer be used.
- ep_expire_sign: Timestamp;
-
- // Time when this online signing key legally expires.
- ep_expire_legal: Timestamp;
- }
-
- .. note::
-
- This API is still experimental (and is not yet implemented at the
- time of this writing).
-
.. _deposit-confirmation:
---------------------
@@ -257,167 +205,2392 @@ paid out first.
time of this writing). A key open question is whether the auditor
should sign the response information.
-This API is used by the auditor to obtain a list of all deposit confirmations that the auditor
-did not receive by the exchange, only by the merchant.
-.. http:get:: /deposit-confirmation
+.. _spa-api:
+
+---------------------------
+Monitoring API
+---------------------------
+
+The following entries specify how to access the results of an audit.
+
+For most endpoints, rows may be marked as 'suppressed', to not send them again upon subsequent GET requests.
+To do this, a :ts:type:`GenericUpdate` object may be used.
+
+**Details:**
+
+ .. ts:def:: GenericUpdate
+
+ interface GenericUpdate {
+
+ // if true, subsequent GET requests will not this element by default
+ suppressed : boolean;
+
+ // unused
+ ancient? : boolean;
+
+ }
+
+
+
+.. fee-time-inconsistency-list:
- Get a list of all deposit confirmations that were not received by the auditor from the exchange to be manually audited.
+Fee Time Inconsistencies
+------------------------
+
+This section highlights cases, where validity periods associated with wire fees the
+exchange may charge merchants, are invalid.
+This usually means that a start date of a fee is set to a later date than its end date.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/fee-time-inconsistency
+
+ Get a list of fee time inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`DepositConfirmationList` object.
- :http:statuscode:`204 No Content`:
- No missing deposit confirmations found.
+ The auditor responds with a top level array of :ts:type:`FeeTimeInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: DepositConfirmationList
+ .. ts:def:: FeeTimeInconsistency
- interface DepositConfirmationList {
- // Deposit confirmations to be audited.
- deposit-confirmations: DepositConfirmation[];
+ interface FeeTimeInconsistency {
+
+
+ row_id : Integer;
+
+ type : string;
+
+ time : Timestamp;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
}
- .. ts:def:: DepositConfirmation
+ .. note::
- interface DepositConfirmation {
+ This API is still experimental. The API will be further developed as needed.
- // Database row id of entry
- row_id: Integer;
- // Time when the deposit confirmation confirmation was generated.
- timestamp: Timestamp;
- // How much time does the merchant have to issue a refund
- // request? Zero if refunds are not allowed.
- refund_deadline: Timestamp;
- // By what time does the exchange have to wire the funds?
- wire_deadline: Timestamp;
+This API is used to suppress select elements of fee time inconsistencies
- // Amount to be deposited, excluding fee. Calculated from the
- // amount with fee and the fee from the deposit request.
- amount_without_fee: Amount;
+.. http:patch:: /monitoring/fee-time-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a fee time inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. emergency-by-count-list:
+
+Emergencies By Count
+--------------------
+
+This API is used to obtain a list of emergencies by count.
+
+Emergencies are errors where more coins were deposited than the
+exchange remembers issuing. This usually means that the private keys
+of the exchange were compromised (stolen or factored) and subsequently
+used to sign coins off the books. If this happens, all coins of the
+respective denomination that the exchange has redeemed so far may have
+been created by the attacker, and the exchange would have to refund
+all of the outstanding coins from ordinary users. Thus, the risk
+exposure is the amount of coins in circulation for a particular
+denomination and the maximum loss for the exchange from this type of
+compromise.
+
+.. http:get:: /monitoring/emergency-by-count
+
+ Get a list of emergencies by count stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`EmergencyByCount` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: EmergencyByCount
+
+ interface EmergencyByCount {
+
+
+ row_id : Integer;
+
+ denompub_h : HashCode;
+
+ num_issued : Integer;
+
+ num_known : Integer;
+
+ risk : Amount;
+
+ start : Timestamp;
+
+ deposit_end : Timestamp;
+
+ value : Amount;
+
+ suppressed : boolean;
+
+
}
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
+
+
-This API is used by the auditor to delete an audited deposit confirmation.
-.. http:delete:: /deposit-confirmation/$SERIAL_ID
+This API is used to suppress select elements of emergencies by count
- Delete deposit confirmation entry with given serial_id.
+.. http:patch:: /monitoring/emergency-by-count/$SERIAL_ID
+
+ Update the 'suppressed' field of an emergency by count element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
**Response:**
- :http:statuscode:`204 No content`:
- The deposit confirmation was deleted.
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
- :http:statuscode:`401 Unauthorized`:
- Unauthorized request.
+.. row-inconsistency-list:
- :http:statuscode:`404 Not found`:
- The deposit confirmation was unknown.
+Row Inconsistencies
+-------------------
+
+This section highlights serious row inconsistencies of the exchange.
+Row inconsistencies are reported from different sources,
+and might point data that is (suddenly) missing from the exchange, or has been manipulated in
+illogical ways. The affected table is noted in the 'table' field. A description of the nature of the
+inconsistency is noted in 'diagnostic'.
+
+#FIXME: this might be inaccurate
+
+
+.. http:get:: /monitoring/row-inconsistency
+
+ Get a list of row inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`RowInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: RowInconsistency
+
+ interface RowInconsistency {
+
+
+ row_id : Integer;
+
+ row_table : string;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
-.. balances-list:
+
+
+This API is used to suppress select elements of row inconsistencies
+
+.. http:patch:: /monitoring/row-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a row inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-in-inconsistency-list:
+
+Reserve In Inconsistencies
+--------------------------
+
+This section lists cases where the exchange's and auditor's expectation of amounts transerred
+into a reserve differs.
+
+#FIXME: not sure if this is accurate
+
+.. http:get:: /monitoring/reserve-in-inconsistency
+
+ Get a list of reserve in inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveInInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveInInconsistency
+
+ interface ReserveInInconsistency {
+
+
+ row_id : Integer;
+
+ amount_exchange_expected : Amount;
+
+ amount_wired : Amount;
+
+ reserve_pub : EddsaPublicKey;
+
+ timestamp : Timestamp;
+
+ account : string;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of reserve in inconsistencies
+
+.. http:patch:: /monitoring/reserve-in-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a reserve in inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. purse-not-closed-inconsistencies-list:
+
+Purse Not Closed Inconsistencies
+--------------------------------
+
+This section highlights cases, in which either payer or payee did not finish their part of a transaction,
+causing a purse – which may contain some currency – to expire.
+
+#FIXME: this might be inaccurate
+
+
+.. http:get:: /monitoring/purse-not-closed-inconsistencies
+
+ Get a list of purse not closed inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`PurseNotClosedInconsistencies` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: PurseNotClosedInconsistencies
+
+ interface PurseNotClosedInconsistencies {
+
+
+ row_id : Integer;
+
+ purse_pub : EddsaPublicKey;
+
+ amount : Amount;
+
+ expiration_date : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of purse not closed inconsistencies
+
+.. http:patch:: /monitoring/purse-not-closed-inconsistencies/$SERIAL_ID
+
+ Update the 'suppressed' field of a purse not closed inconsistencies element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-not-closed-inconsistency-list:
+
+Reserve Not Closed Inconsistencies
----------------------------------
-Obtaining list of auditor balances
-----------------------------------
-This API is used to obtain a list of all the balances that are stored by the auditor.
+This section highlights cases, in which reserves were not closed, despite being expired.
+
+.. http:get:: /monitoring/reserve-not-closed-inconsistency
+
+ Get a list of reserve not closed inconsistencies stored by the auditor.
-.. http:get:: /balances
+ The following query parameters are optional, and can be used to customise the response:
- Get a list of all balances stored by the auditor.
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`BalanceList` object.
+ The auditor responds with a top level array of :ts:type:`ReserveNotClosedInconsistency` objecs.
- :http:statuscode:`409 Conflict`:
- Balance missing or other error occured.
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: BalanceList
+ .. ts:def:: ReserveNotClosedInconsistency
+
+ interface ReserveNotClosedInconsistency {
- interface BalanceList {
- // Total amount reported
- auditor_total_reported_balance: Amount;
+
+ row_id : Integer;
- // Amount potentially missing
- auditor_missing_balance: Amount;
+ reserve_pub : EddsaPublicKey;
- //...
+ balance : Amount;
+
+ expiration_time : Timestamp;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
}
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing). The API will be further developed as needed.
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of reserve not closed inconsistencies
+
+.. http:patch:: /monitoring/reserve-not-closed-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a reserve not closed inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-balance-insufficient-inconsistency-list:
+
+Reserve Balance Insufficient Inconsistencies
+--------------------------------------------
+
+This section highlights cases where more coins were withdrawn from a
+reserve than the reserve contained funding for. This is a serious
+compromise resulting in proportional financial losses to the exchange.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/reserve-balance-insufficient-inconsistency
+
+ Get a list of reserve balance insufficient inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveBalanceInsufficientInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveBalanceInsufficientInconsistency
+
+ interface ReserveBalanceInsufficientInconsistency {
+
+
+ row_id : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ inconsistency_gain : boolean;
+
+ inconsistency_amount : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of reserve balance insufficient inconsistencies
-.. denominations-pending-list:
+.. http:patch:: /monitoring/reserve-balance-insufficient-inconsistency/$SERIAL_ID
----------------------------------------
-Obtaining list of pending denominations
----------------------------------------
+ Update the 'suppressed' field of a reserve balance insufficient inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
-This API is used by the auditor to obtain a list of pending denominations
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. invalid-signature-losses-list:
+
+Invalid Signature Losses
+--------------
-.. http:get:: /pending-denominations
+This section lists operations that the exchange performed, but for
+which the signatures provided are invalid. Hence the operations were
+invalid and the amount involved should be considered lost.
- Get a list of all pending denominations.
+.. http:get:: /monitoring/bad-sig-losses
+
+ Get a list of invalid signature losses stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+ :query operation: A string. If specified, only returns eligible rows with this :ts:type:`BadSigLosses`.operation value. The default value is NULL.
+ :query use_op_spec_pub: A boolean. If true, use the value of :ts:type:`OpSpecPub` to only return eligible rows with this :ts:type:`BadSigLosses`.operation_specific_pub value. The default value is NULL.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
**Response:**
:http:statuscode:`200 OK`:
- The auditor responds with a :ts:type:`PendingDenominationList` object.
+ The auditor responds with a top level array of :ts:type:`BadSigLosses` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
- :http:statuscode:`204 No content`:
- No pending demoninations found.
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
**Details:**
- .. ts:def:: PendingDenominationList
+ .. ts:def:: BadSigLosses
+
+ interface BadSigLosses {
+
+
+ row_id : Integer;
- interface PendingDenominationList {
- // PendingDenominationList of the auditor.
- pending_denomination: PendingDenomination[];
+ operation : string;
+
+ loss : Amount;
+
+ operation_specific_pub : EddsaPublicKey;
+
+ suppressed : boolean;
+
+
}
- .. ts:def:: PendingDenomination
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of bad sig losses
+
+.. http:patch:: /monitoring/bad-sig-losses/$SERIAL_ID
+
+ Update the 'suppressed' field of a bad sig losses element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
- interface PendingDenomination {
- // Balance of denomination.
- denom_balance: Amount;
+.. coin-inconsistency-list:
- // Amount that was lost due to failures by the exchange.
- denom_loss: Amount;
+Coin Inconsistencies
+--------------------
- // Amount at risk of loss due to recoup operations.
- denom_risk: Amount;
+This section lists cases where the exchange made arithmetic errors found when
+looking at the transaction history of a coin. The totals sum up the differences
+in amounts that matter for profit/loss calculations of the exchange. When an
+exchange merely shifted money from customers to merchants (or vice versa) without
+any effects on its own balance, those entries are excluded from the total.
- // Amount actually lost due to recoup operations after a revocation.
- recoup_loss: Amount;
+.. http:get:: /monitoring/coin-inconsistency
+
+ Get a list of coin inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`CoinInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: CoinInconsistency
+
+ interface CoinInconsistency {
+
+
+ row_id : Integer;
+
+ operation : string;
+
+ exchange_amount : Amount;
+
+ auditor_amount : Amount;
+
+ coin_pub : EddsaPublicKey;
+
+ profitable : boolean;
+
+ suppressed : boolean;
+
+
}
.. note::
- This API is still experimental (and is not yet implemented at the
- time of this writing).
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of coin inconsistencies
+
+.. http:patch:: /monitoring/coin-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a coin inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. balances-list:
+
+Balances
+--------
+
+Unlike many other sections of the auditor, entries in the balances table do not necessarily represent misbehaviour
+or unusual findings. Instead, the auditor keeps its own record of the current balances of purses, to compare them with the
+purse balances.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/balances
+
+ Get a list of balances stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+ :query balance_key: a string identifying a balance. If specified, only returns elements with this exact key. The default value is NULL.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Balances` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Balances
+
+ interface Balances {
+
+
+ row_id : Integer;
+
+ balance_key : string;
+
+ balance_value : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of balances
+
+.. http:patch:: /monitoring/balances/$SERIAL_ID
+
+ Update the 'suppressed' field of a balances element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denominations-without-signatures-list:
+
+Denominations Without Signatures
+--------------------------------
+
+This section highlights denomination keys that lack a proper
+signature from the taler-auditor-offline tool. This may be
+legitimate, say in case where the auditor's involvement in the
+exchange business is ending and a new auditor is responsible for
+future denominations. So this must be read with a keen eye on the
+business situation.
+
+.. http:get:: /monitoring/denominations-without-sigs
+
+ Get a list of denominations without signatures stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DenominationsWithoutSigs` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DenominationsWithoutSigs
+
+ interface DenominationsWithoutSigs {
+
+
+ row_id : Integer;
+
+ denompub_h : HashCode;
+
+ value : Amount;
+
+ start_time : Timestamp;
+
+ end_time : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of denominations without sigs
+
+.. http:patch:: /monitoring/denominations-without-sigs/$SERIAL_ID
+
+ Update the 'suppressed' field of a denominations without signatures element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. misattribution-in-inconsistency-list:
+
+Misattribution In Inconsistencies
+---------------------------------
+
+This section lists cases where the sender account record of an
+incoming wire transfer differs between the exchange and the bank.
+This will cause funds to be sent to the wrong account when the reserve
+is closed and the remaining balance is refunded to the original
+account.
+
+#FIXME: not sure if this is accurate
+
+.. http:get:: /monitoring/misattribution-in-inconsistency
+
+ Get a list of misattribution in inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`MisattributionInInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: MisattributionInInconsistency
+
+ interface MisattributionInInconsistency {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ bank_row : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of misattribution in inconsistencies
+
+.. http:patch:: /monitoring/misattribution-in-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of an misattribution in inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+.. reserves-list:
+
+
+
+
+Reserves
+--------
+
+This API is used to obtain a list of reserves
+
+#FIXME: this is missing some information
+
+.. http:get:: /monitoring/reserves
+
+ Get a list of reserves stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Reserves` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Reserves
+
+ interface Reserves {
+
+
+ auditor_reserves_rowid : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ reserve_balance : Amount;
+
+ reserve_loss : Amount;
+
+ withdraw_fee_balance : Amount;
+
+ close_fee_balance : Amount;
+
+ purse_fee_balance : Amount;
+
+ open_fee_balance : Amount;
+
+ history_fee_balance : Amount;
+
+ expiration_date : Timestamp;
+
+ origin_account : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of reserves
+
+.. http:patch:: /monitoring/reserves/$SERIAL_ID
+
+ Update the 'suppressed' field of an reserves element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. deposit-confirmations-list:
+
+Deposit Confirmations
+---------------------
+
+This section contains a list of deposits the merchant made to the exchange, provided by a merchant.
+This list can be cross-referenced with the exchanges deposits; any discrepancies could be a sign of misbehaviour.
+
+.. http:get:: /monitoring/deposit-confirmations
+
+ Get a list of deposit confirmations stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DepositConfirmations` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DepositConfirmations
+
+ interface DepositConfirmations {
+
+
+ deposit_confirmation_serial_id : Integer;
+
+ h_contract_terms : HashCode;
+
+ h_policy : HashCode;
+
+ h_wire : HashCode;
+
+ exchange_timestamp : Timestamp;
+
+ refund_deadline : Timestamp;
+
+ wire_deadline : Timestamp;
+
+ total_without_fee : Amount;
+
+ coin_pubs : EddsaPublicKey;
+
+ coin_sigs : EddsaSignature;
+
+ merchant_pub : EddsaPublicKey;
+
+ exchange_sig : EddsaSignature;
+
+ exchange_pub : EddsaPublicKey;
+
+ master_sig : EddsaSignature;
+
+ suppressed : boolean;
+
+ ancient : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of deposit confirmations
+
+.. http:patch:: /monitoring/deposit-confirmations/$SERIAL_ID
+
+ Update the 'suppressed' field of an deposit confirmations element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+.. denomination-key-validity-withdraw-inconsistency-list:
+
+Denomination Key Validity Withdraw Inconsistencies
+--------------------------------------------------
+
+This section highlights cases, where denomination keys were used to withdraw coins from a reserve,
+after they were already expired.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/denomination-key-validity-withdraw-inconsistency
+
+ Get a list of denomination key validity withdraw inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DenominationKeyValidityWithdrawInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DenominationKeyValidityWithdrawInconsistency
+
+ interface DenominationKeyValidityWithdrawInconsistency {
+
+
+ row_id : Integer;
+
+ execution_date : Timestamp;
+
+ reserve_pub : EddsaPublicKey;
+
+ denompub_h : HashCode;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of denomination key validity withdraw inconsistencies
+
+.. http:patch:: /monitoring/denomination-key-validity-withdraw-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a denomination key validity withdraw inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. purses-list:
+
+Purses
+------
+
+This section highlights cases, in which a purses summary (balance) does
+not match the balance an exchange expects it to be.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/purses
+
+ Get a list of purses stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Purses` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Purses
+
+ interface Purses {
+
+
+ auditor_purses_rowid : Integer;
+
+ purse_pub : EddsaPublicKey;
+
+ balance : Amount;
+
+ target : Amount;
+
+ expiration_date : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of purses
+
+.. http:patch:: /monitoring/purses/$SERIAL_ID
+
+ Update the 'suppressed' field of a purses element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. amount-arithmetic-inconsistency-list:
+
+Amount Arithmetic Inconsistencies
+---------------------------------
+
+This API is used to obtain a list of amount arithmetic inconsistencies
+
+This section lists cases where the arithmetic of the exchange
+involving amounts disagrees with the arithmetic of the auditor.
+Disagreements imply that either the exchange made a loss (sending out
+too much money), or screwed a customer (and thus at least needs to fix
+the financial damage done to the customer).
+The profitable column is set to true if the arithmetic problem was be determined to be
+profitable for the exchange, false if the problem resulted in a net loss for
+the exchange.
+
+.. http:get:: /monitoring/amount-arithmetic-inconsistency
+
+ Get a list of amount arithmetic inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`AmountArithmeticInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: AmountArithmeticInconsistency
+
+ interface AmountArithmeticInconsistency {
+
+
+ row_id : Integer;
+
+ operation : string;
+
+ exchange_amount : Amount;
+
+ auditor_amount : Amount;
+
+ profitable : boolean;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of amount arithmetic inconsistencies
+
+.. http:patch:: /monitoring/amount-arithmetic-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of an amount arithmetic inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. historic-denomination-revenue-list:
+
+Historic Denomination Revenue
+-----------------------------
+
+This API is used to obtain a list of historic denomination revenue
+
+#FIXME: this is missing some information
+
+.. http:get:: /monitoring/historic-denomination-revenue
+
+ Get a list of historic denomination revenue stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`HistoricDenominationRevenue` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: HistoricDenominationRevenue
+
+ interface HistoricDenominationRevenue {
+
+
+ denom_pub_hash : HashCode;
+
+ revenue_timestamp : Timestamp;
+
+ revenue_balance : Amount;
+
+ loss_balance : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of historic denomination revenue
+
+.. http:patch:: /monitoring/historic-denomination-revenue/$SERIAL_ID
+
+ Update the 'suppressed' field of a historic denomination revenue element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. denomination-pending-list:
+
+Denomination Pending
+--------------------
+
+This API is used to obtain a list of denomination pending
+
+#FIXME: this is missing some information
+
+.. http:get:: /monitoring/denomination-pending
+
+ Get a list of denomination pending stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`DenominationPending` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: DenominationPending
+
+ interface DenominationPending {
+
+
+ denom_pub_hash : HashCode;
+
+ denom_balance : Amount;
+
+ denom_loss : Amount;
+
+ num_issued : Integer;
+
+ denom_risk : Amount;
+
+ recoup_loss : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of denomination pending
+
+.. http:patch:: /monitoring/denomination-pending/$SERIAL_ID
+
+ Update the 'suppressed' field of a denomination pending element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. historic-reserve-summary-list:
+
+Historic Reserve Summary
+------------------------
+
+This section highlights cases, where the exchanges expectation of the summary in a reserve
+differs from its actual summary.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/historic-reserve-summary
+
+ Get a list of historic reserve summary stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`HistoricReserveSummary` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: HistoricReserveSummary
+
+ interface HistoricReserveSummary {
+
+
+ row_id : Integer;
+
+ start_date : Timestamp;
+
+ end_date : Timestamp;
+
+ reserve_profits : Amount;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of historic reserve summary
+
+.. http:patch:: /monitoring/historic-reserve-summary/$SERIAL_ID
+
+ Update the 'suppressed' field of a historic reserve summary element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. exchange-signkeys-list:
+
+Exchange Signkeys
+-----------------
+
+This API is used to obtain a list of exchange signkeys
+
+#FIXME: this is missing some information
+
+.. http:get:: /monitoring/exchange-signkeys
+
+ Get a list of exchange signkeys stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ExchangeSignkeys` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ExchangeSignkeys
+
+ interface ExchangeSignkeys {
+
+
+ exchange_pub : EddsaPublicKey;
+
+ master_sig : EddsaSignature;
+
+ ep_valid_from : Timestamp;
+
+ ep_expire_sign : Timestamp;
+
+ ep_expire_legal : Timestamp;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of exchange signkeys
+
+.. http:patch:: /monitoring/exchange-signkeys/$SERIAL_ID
+
+ Update the 'suppressed' field of an exchange signkeys element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. wire-format-inconsistency-list:
+
+Wire Format Inconsistencies
+---------------------------
+
+This section highlights cases, where the format of a wire transaction was malformed in some way.
+
+.. http:get:: /monitoring/wire-format-inconsistency
+
+ Get a list of wire format inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`WireFormatInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: WireFormatInconsistency
+
+ interface WireFormatInconsistency {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ wire_offset : Integer;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of wire format inconsistencies
+
+.. http:patch:: /monitoring/wire-format-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a wire format inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. refreshes-hanging-list:
+
+Refreshes Hanging
+-----------------
+
+This section highlights cases, where refreshed (and then revealed) coins could not be found in an exchanges database.
+This might happen, even if the exchange is operating correctly. In that case, the exchange might simply be slower than
+the auditor.
+
+#FIXME: this might be inaccurate
+
+.. http:get:: /monitoring/refreshes-hanging
+
+ Get a list of refreshes hanging stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`RefreshesHanging` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: RefreshesHanging
+
+ interface RefreshesHanging {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ coin_pub : EddsaPublicKey;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of refreshes hanging
+
+.. http:patch:: /monitoring/refreshes-hanging/$SERIAL_ID
+
+ Update the 'suppressed' field of a refreshes hanging element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. emergency-list:
+
+Emergencies
+-----------
+
+This API is used to obtain a list of emergencies.
+
+Emergencies are errors where more coins were deposited than the
+exchange remembers issuing. This usually means that the private keys
+of the exchange were compromised (stolen or factored) and subsequently
+used to sign coins off the books. If this happens, all coins of the
+respective denomination that the exchange has redeemed so far may have
+been created by the attacker, and the exchange would have to refund
+all of the outstanding coins from ordinary users. Thus, the risk
+exposure is the amount of coins in circulation for a particular
+denomination and the maximum loss for the exchange from this type of
+compromise.
+
+#FIXME: what is the difference between Emergencies and Emergencies by Count
+
+.. http:get:: /monitoring/emergency
+
+ Get a list of emergencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`Emergency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: Emergency
+
+ interface Emergency {
+
+
+ row_id : Integer;
+
+ denompub_h : HashCode;
+
+ denom_risk : Amount;
+
+ denom_loss : Amount;
+
+ deposit_start : Timestamp;
+
+ deposit_end : Timestamp;
+
+ value : Amount;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of emergencies
+
+.. http:patch:: /monitoring/emergency/$SERIAL_ID
+
+ Update the 'suppressed' field of an emergency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. closure-lags-list:
+
+Closure Lags
+------------
+
+This API is used to obtain a list of closure lags.
+
+Lag can be due to some component being behind in
+executing transactions. This is usually either the exchange's aggregator, the
+bank's wire transfer logic, or the synchronization of databases between
+exchange and auditor. Significant lag may be indicative of fraud, while
+moderate lag is indicative that the systems may be too slow to handle the
+load. Small amounts of lag can occur in normal operation.
+
+.. http:get:: /monitoring/closure-lags
+
+ Get a list of closure lags stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ClosureLags` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ClosureLags
+
+ interface ClosureLags {
+
+
+ row_id : Integer;
+
+ amount : Amount;
+
+ deadline : Timestamp;
+
+ wtid : Integer;
+
+ account : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of closure lags
+
+.. http:patch:: /monitoring/closure-lags/$SERIAL_ID
+
+ Update the 'suppressed' field of a closure lags element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. wire-out-inconsistency-list:
+
+Wire Out Inconsistencies
+------------------------
+
+This section highlights cases where the exchange's record about
+outgoing wire transfers does not match with that of the bank.
+
+.. http:get:: /monitoring/wire-out-inconsistency
+
+ Get a list of wire out inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`WireOutInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: WireOutInconsistency
+
+ interface WireOutInconsistency {
+
+
+ row_id : Integer;
+
+ destination_account : string;
+
+ expected : Amount;
+
+ claimed : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of wire out inconsistencies
+
+.. http:patch:: /monitoring/wire-out-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a wire out inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. reserve-balance-summary-wrong-inconsistency-list:
+
+Reserve Balance Summary Wrong Inconsistencies
+---------------------------------------------
+
+This section highlights cases, where the exchange's and auditors'
+expectation of the amount of money in a reserve differs.
+
+.. http:get:: /monitoring/reserve-balance-summary-wrong-inconsistency
+
+ Get a list of reserve balance summary wrong inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`ReserveBalanceSummaryWrongInconsistency` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: ReserveBalanceSummaryWrongInconsistency
+
+ interface ReserveBalanceSummaryWrongInconsistency {
+
+
+ row_id : Integer;
+
+ reserve_pub : EddsaPublicKey;
+
+ exchange_amount : Amount;
+
+ auditor_amount : Amount;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of reserve balance summary wrong inconsistencies
+
+.. http:patch:: /monitoring/reserve-balance-summary-wrong-inconsistency/$SERIAL_ID
+
+ Update the 'suppressed' field of a reserve balance summary wrong inconsistency element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+.. row-minor-inconsistencies-list:
+
+Row Minor Inconsistencies
+-------------------------
+
+The section highlights inconsistencies, which are cause for concern,
+but not necessarily point to a monetary loss (yet).
+
+#FIXME: might not be accurate
+
+.. http:get:: /monitoring/row-minor-inconsistencies
+
+ Get a list of row minor inconsistencies stored by the auditor.
+
+ The following query parameters are optional, and can be used to customise the response:
+
+ **Request:**
+
+ :query limit: A signed integer, indicating how many elements relative to the offset query parameter should be returned. The default value is -20.
+ :query offset: An unsigned integer, indicating from which row onward to return elements. The default value is INT_MAX.
+ :query return_suppressed: A boolean. If true, returns all eligible rows, otherwise only returns eligible rows that are not suppressed. The default value is false.
+
+
+ The default values, thus, return at max the 20 latest elements that are not suppressed.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The auditor responds with a top level array of :ts:type:`RowMinorInconsistencies` objecs.
+
+ :http:statuscode:`403 Forbidden`:
+ No or bad Bearer token provided.
+
+ :http:statuscode:`404 Not Found`:
+ No elements could be found.
+
+ **Details:**
+
+ .. ts:def:: RowMinorInconsistencies
+
+ interface RowMinorInconsistencies {
+
+
+ row_id : Integer;
+
+ row_table : Integer;
+
+ diagnostic : string;
+
+ suppressed : boolean;
+
+
+ }
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
+
+This API is used to suppress select elements of row minor inconsistencies
+
+.. http:patch:: /monitoring/row-minor-inconsistencies/$SERIAL_ID
+
+ Update the 'suppressed' field of a row minor inconsistencies element with row_id $SERIAL_ID, according to :ts:type:`GenericUpdate`, stored by the auditor.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ The element has been accepted for processing.
+
+ .. note::
+
+ This API is still experimental. The API will be further developed as needed.
+
+
+
----------
Complaints
diff --git a/core/api-bank-conversion-info.rst b/core/api-bank-conversion-info.rst
index 322e9403..63856142 100644
--- a/core/api-bank-conversion-info.rst
+++ b/core/api-bank-conversion-info.rst
@@ -108,7 +108,7 @@ is used by wallets for withdrawals that involve a currency conversion.
* ``TALER_EC_GENERIC_PARAMETER_MALFORMED`` : both of the parameters have been provided or one of them is not a valid Taler amount.
* ``TALER_EC_GENERIC_CURRENCY_MISMATCH`` : the parameter is in the wrong currency.
:http:statuscode:`409 Conflict`:
- The amount is too small to be converted, either because it produces produce an amount less than zero, or because the server requires a higher minimum amount than that supplied.
+ The amount is too small to be converted because it produces an amount less than zero.
:http:statuscode:`501 Not implemented`:
This server does not support conversion, client should check config response.
@@ -154,7 +154,7 @@ is used by wallets for withdrawals that involve a currency conversion.
* ``TALER_EC_GENERIC_PARAMETER_MALFORMED`` : both of the parameters have been provided or one of them is not a valid Taler amount.
* ``TALER_EC_GENERIC_CURRENCY_MISMATCH`` : the parameter is in the wrong currency.
:http:statuscode:`409 Conflict`:
- The amount is too small to be converted, either because it produces produce an amount less than zero, or because the server requires a higher minimum amount than that supplied.
+ The amount is too small to be converted because it produces an amount less than zero.
:http:statuscode:`501 Not implemented`:
This server does not support conversion, client should check config response.
diff --git a/core/api-bank-integration.rst b/core/api-bank-integration.rst
index e7db1efc..3efe6ff3 100644
--- a/core/api-bank-integration.rst
+++ b/core/api-bank-integration.rst
@@ -107,6 +107,11 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
// @since **v1**
status: "pending" | "selected" | "aborted" | "confirmed";
+ // Currency used for the withdrawal.
+ // MUST be present when amount is absent.
+ // @since v2, may become mandatory in the future.
+ currency?: string;
+
// Amount that will be withdrawn with this operation
// (raw amount without fee considerations). Only
// given once the amount is fixed and cannot be changed.
@@ -243,6 +248,7 @@ for the withdrawal operation (the ``WITHDRAWAL_ID``) to interact with the withdr
// It may contain withdrawal operation id
confirm_transfer_url?: string;
+ // @deprecated since **v1**, use ``status`` instead
// The transfer has been confirmed and registered by the bank.
// Does not guarantee that the funds have arrived at the exchange already.
transfer_done: boolean;
diff --git a/core/api-bank-wire.rst b/core/api-bank-wire.rst
index 34658f01..84184ec9 100644
--- a/core/api-bank-wire.rst
+++ b/core/api-bank-wire.rst
@@ -440,9 +440,11 @@ Security Considerations
=======================
For implementors:
+
* The withdrawal operation ID must contain enough entropy to be unguessable.
Design:
+
* The user must complete the 2FA step of the withdrawal in the context of their banking
app or online banking Website.
We explicitly reject any design where the user would have to enter a confirmation code
diff --git a/core/api-challenger.rst b/core/api-challenger.rst
index afb3096d..f4344b03 100644
--- a/core/api-challenger.rst
+++ b/core/api-challenger.rst
@@ -320,7 +320,7 @@ Challenge
.. ts:def:: ChallengeResponse
- interface ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
+ type ChallengeResponse = ChallengeRedirect | ChallengeCreateResponse
.. ts:def:: ChallengeRedirect
diff --git a/core/api-common.rst b/core/api-common.rst
index 521b2fc0..afd1063f 100644
--- a/core/api-common.rst
+++ b/core/api-common.rst
@@ -228,7 +228,7 @@ This section describes how certain types of values are represented throughout th
Binary Data
^^^^^^^^^^^
-.. ts:def:: foobase
+.. ts:def:: Base32
type Base32 = string;
@@ -255,6 +255,11 @@ hashed data. See `base32`_.
// 32-byte hash code.
type ShortHashCode = string;
+.. ts:def:: AccountAccessToken
+
+ // 32-byte nonce.
+ type AccountAccessToken = string;
+
.. ts:def:: WireSalt
// 16-byte salt.
@@ -758,6 +763,56 @@ denoting microseconds since the UNIX Epoch. ``UINT64_MAX`` represents "never".
uint64_t abs_value_us__; // in network byte order
};
+.. _LegalTrouble:
+
+451 Responses
+^^^^^^^^^^^^^
+
+When KYC operations are required, various endpoints may respond with a
+``451 Unavailable for Legal Reasons`` status code and a `LegitimizationNeededResponse`
+body.
+
+.. ts:def:: LegitimizationNeededResponse
+
+ // Implemented in this style since exchange
+ // protocol **v20**.
+ interface LegitimizationNeededResponse {
+
+ // Numeric `error code <error-codes>` unique to the condition.
+ // Should always be ``TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED``.
+ code: number;
+
+ // Human-readable description of the error, i.e. "missing parameter",
+ // "commitment violation", ... Should give a human-readable hint
+ // about the error's nature. Optional, may change without notice!
+ hint?: string;
+
+ // Hash of the payto:// account URI for which KYC
+ // is required.
+ h_payto: PaytoHash;
+
+ // Public key associated with the account. The client must sign
+ // the initial request for the KYC status using the corresponding
+ // private key. Will be either a reserve public key or a merchant
+ // (instance) public key.
+ //
+ // Absent if no public key is currently associated
+ // with the account and the client MUST thus first
+ // credit the exchange via an inbound wire transfer
+ // to associate a public key with the debited account.
+ account_pub?: EddsaPublicKey;
+
+ // Identifies a set of measures that were triggered and that are
+ // now preventing this operation from proceeding. Gives the
+ // account holder a starting point for understanding why the
+ // transaction was blocked and how to lift it. The account holder
+ // should use the number to check for the account's AML/KYC status
+ // using the ``/kyc-check/$REQUIREMENT_ROW`` endpoint.
+ requirement_row: Integer;
+
+ }
+
+
Cryptographic primitives
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -823,7 +878,6 @@ uses 512-bit hash codes (64 bytes).
};
-
.. _TALER_EcdhEphemeralPublicKeyP:
.. sourcecode:: c
diff --git a/core/api-corebank.rst b/core/api-corebank.rst
index 4a0288a4..6a7bec8b 100644
--- a/core/api-corebank.rst
+++ b/core/api-corebank.rst
@@ -188,6 +188,11 @@ Account Management
// Only admin can set this property.
debit_threshold?: Amount;
+ // If present, set a custom minimum cashout amount for this account.
+ // Only admin can set this property
+ // @since v4
+ min_cashout?: Amount;
+
// If present, enables 2FA and set the TAN channel used for challenges
// Only admin can set this property, other user can reconfig their account
// after creation.
@@ -220,6 +225,7 @@ Account Management
* ``TALER_EC_BANK_UNALLOWED_DEBIT`` : admin account does not have sufficient funds to grant bonus.
* ``TALER_EC_BANK_RESERVED_USERNAME_CONFLICT`` : a reserved username was attempted, like ``admin`` or ``bank``
* ``TALER_EC_BANK_NON_ADMIN_PATCH_DEBT_LIMIT`` : a non-admin user has tried to create an account with a customer debt limit.
+ * ``TALER_EC_BANK_NON_ADMIN_SET_MIN_CASHOUT`` : a non-admin user has tried to create an account with a custom min cashout amount.
* ``TALER_EC_BANK_NON_ADMIN_SET_TAN_CHANNEL`` : a non-admin user has tried to create an account with 2fa.
* ``TALER_EC_BANK_TAN_CHANNEL_NOT_SUPPORTED``: ``tan_channel`` is not supported, check bank config to find supported ones.
* ``TALER_EC_BANK_MISSING_TAN_INFO``: the user did not share any contact data where to send the TAN via ``tan_channel``.
@@ -233,28 +239,6 @@ Account Management
internal_payto_uri: string;
}
-.. _delete-account:
-
-.. http:delete:: /accounts/$USERNAME
-
- Delete the account whose username is ``$USERNAME``. The deletion
- succeeds only if the balance is *zero*. Typically only available to
- the administrator, but can be configured to allow ordinary users too.
-
- **Response:**
-
- :http:statuscode:`202 Accepted`:
- 2FA is required for this operation. This returns the `Challenge` response.
- :http:statuscode:`204 No content`:
- The account was successfully deleted.
- :http:statuscode:`401 Unauthorized`:
- Invalid credentials or missing rights.
- :http:statuscode:`404 Not found`:
- The account pointed by ``$USERNAME`` was not found.
- :http:statuscode:`409 Conflict`:
- * ``TALER_EC_BANK_RESERVED_USERNAME_CONFLICT`` : a reserved username was attempted, like ``admin`` or ``bank``.
- * ``TALER_EC_BANK_ACCOUNT_BALANCE_NOT_ZERO``: the account balance was not zero.
-
.. _account-reconfig:
.. http:patch:: /accounts/$USERNAME
@@ -288,6 +272,11 @@ Account Management
// Only admin can change this property.
debit_threshold?: Amount;
+ // If present, change the custom minimum cashout amount for this account.
+ // Only admin can set this property
+ // @since v4
+ min_cashout?: Amount;
+
// If present, enables 2FA and set the TAN channel used for challenges
tan_channel?: TanChannel;
}
@@ -306,6 +295,7 @@ Account Management
* ``TALER_EC_BANK_NON_ADMIN_PATCH_LEGAL_NAME`` : a non-admin user has tried to change their legal name.
* ``TALER_EC_BANK_NON_ADMIN_PATCH_CASHOUT`` : a non-admin user has tried to change their cashout account.
* ``TALER_EC_BANK_NON_ADMIN_PATCH_DEBT_LIMIT`` : a non-admin user has tried to change their debt limit.
+ * ``TALER_EC_BANK_NON_ADMIN_SET_MIN_CASHOUT`` : a non-admin user has tried to change their custom min cashout amount.
* ``TALER_EC_BANK_TAN_CHANNEL_NOT_SUPPORTED`` : ``tan_channel`` is not supported, check bank config to find supported ones.
* ``TALER_EC_BANK_MISSING_TAN_INFO`` : the user did not share any contact data where to send the TAN via ``tan_channel``.
@@ -343,6 +333,29 @@ Account Management
* ``TALER_EC_BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD``: a non-admin user has tried to change their password whihout providing the current one.
* ``TALER_EC_BANK_PATCH_BAD_OLD_PASSWORD`` : provided old password does not match current password.
+
+.. _delete-account:
+
+.. http:delete:: /accounts/$USERNAME
+
+ Delete the account whose username is ``$USERNAME``. The deletion
+ succeeds only if the balance is *zero*. Typically only available to
+ the administrator, but can be configured to allow ordinary users too.
+
+ **Response:**
+
+ :http:statuscode:`202 Accepted`:
+ 2FA is required for this operation. This returns the `Challenge` response.
+ :http:statuscode:`204 No content`:
+ The account was successfully deleted.
+ :http:statuscode:`401 Unauthorized`:
+ Invalid credentials or missing rights.
+ :http:statuscode:`404 Not found`:
+ The account pointed by ``$USERNAME`` was not found.
+ :http:statuscode:`409 Conflict`:
+ * ``TALER_EC_BANK_RESERVED_USERNAME_CONFLICT`` : a reserved username was attempted, like ``admin`` or ``bank``.
+ * ``TALER_EC_BANK_ACCOUNT_BALANCE_NOT_ZERO``: the account balance was not zero.
+
.. _account-list:
.. http:get:: /public-accounts
@@ -431,7 +444,7 @@ Account Management
.. ts:def:: ListBankAccountsResponse
- interfaces ListBankAccountsResponse {
+ interface ListBankAccountsResponse {
accounts: AccountMinimalData[];
}
@@ -460,6 +473,11 @@ Account Management
// Number indicating the max debit allowed for the requesting user.
debit_threshold: Amount;
+ // Custom minimum cashout amount for this account.
+ // If null or absent, the global conversion fee is used.
+ // @since v4
+ min_cashout?: Amount;
+
// Is this account visible to anyone?
is_public: boolean;
@@ -513,6 +531,11 @@ Account Management
// Number indicating the max debit allowed for the requesting user.
debit_threshold: Amount;
+ // Custom minimum cashout amount for this account.
+ // If null or absent, the global conversion fee is used.
+ // @since v4
+ min_cashout?: Amount;
+
// Addresses where to send the TAN for transactions.
// Currently only used for cashouts.
// If missing, cashouts will fail.
@@ -883,9 +906,10 @@ Cashouts
The account pointed by ``$USERNAME`` was not found.
:http:statuscode:`409 Conflict`:
* ``TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED``: an operation with the same ``request_uid`` but different details has been submitted before.
- * ``TALER_EC_BANK_BAD_CONVERSION`` : exchange rate was calculated incorrectly by the client.
- * ``TALER_EC_BANK_UNALLOWED_DEBIT`` : the account does not have sufficient funds.
- * ``TALER_EC_BANK_CONFIRM_INCOMPLETE`` : the user did not share any cashout payto to uri where to wire funds.
+ * ``TALER_EC_BANK_BAD_CONVERSION``: exchange rate was calculated incorrectly by the client.
+ * ``TALER_EC_BANK_BANK_CONVERSION_AMOUNT_TO_SMALL``: the amount of the cashout is too small.
+ * ``TALER_EC_BANK_UNALLOWED_DEBIT``: the account does not have sufficient funds.
+ * ``TALER_EC_BANK_CONFIRM_INCOMPLETE``: the user did not share any cashout payto to uri where to wire funds.
:http:statuscode:`501 Not Implemented`:
* ``TALER_EC_BANK_TAN_CHANNEL_NOT_SUPPORTED``: the chosen ``tan_channel`` is not currently supported.
* This server does not support conversion, client should check config response.
diff --git a/core/api-donau.rst b/core/api-donau.rst
index 09f644ec..dfe7a82e 100644
--- a/core/api-donau.rst
+++ b/core/api-donau.rst
@@ -18,13 +18,15 @@
@author Lukas Matyja
@author Johannes Casaburi
+.. _donau-api:
+
=====================
The Donau RESTful API
=====================
The API specified here follows the :ref:`general conventions <http-common>`
for all details not specified in the individual requests.
-The `glossary <https://docs.taler.net/glossary.html#glossary>`_
+The `glossary <https://docs.taler.net/taler-developer-manual.html#developer-glossary>`_
defines all specific terms used in this section.
.. contents:: Table of Contents
@@ -41,8 +43,9 @@ This is intended to provide a quick overview of the whole REST API. For a more d
The chapters group the families of requests frequently encountered when using the Donau API:
* :ref:`Status information<donau_status>`: get the public signing keys of the Donau, the donation unit key, the Donaus config or some entropy
-* :ref:`Issue receipts<donau_issue>`: For use by charities: Issue receipts for blinded unique donor ids.
-* :ref:`Submit receipts<donau_submit>`: Receive the receipts and, if valid, add all of it's donation units to the donor total. Returns a signature on the total yearly donation amount, hash of taxid+salt and year.
+* :ref:`Issue receipts<donau_issue>`: For use by charities: Issue receipts with blinded unique donor ids from a donor.
+* :ref:`Donation statement<donation_statement>`: Summarizes the donation receipts to the donation statement signature which is made over the total yearly donation amount,
+ the year and the hash of taxid+salt. Provides an API to get the donation statement signature.
* :ref:`Charity administration and status information<donau_charity>`:
* For use by administrators to add/modify a charity
@@ -249,87 +252,167 @@ Issue receipts
Inspired by the Taler exchange :ref:`Withdrawal<exchange-withdrawal>`.
-This API is used by the charity to obtain valid, attested donation receipts from the Donau.
+This API is used to obtain valid, attested donation receipts from the Donau.
Use the :ref:`charity GET route<donau_charity_get>` to see the remaining donation volume for the current year.
+CSR Issue
+~~~~~~~~~~~
+
+.. http:post:: /csr-issue
+
+ Obtain donau-side input values in preparation for a
+ issue receipt step for certain donation unit cipher types,
+ specifically at this point for Clause-Schnorr blind
+ signatures. This API is used by the donor.
+
+ **Request:** The request body must be a `IssuePrepareRequest` object.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `IssuePrepareResponse`. Note that repeating exactly the same request
+ will again yield the same response (assuming none of the donation unit is expired).
+ :http:statuscode:`404 Not found`:
+ The donation unit key is not known to the donau.
+ :http:statuscode:`410 Gone`:
+ The requested donation unit key is not yet or no longer valid. It either before the validity year, past the
+ year or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to
+ understand which of the cases this is and handle it accordingly.
+
+ **Details:**
+
+ .. ts:def:: IssuePrepareRequest
+
+ interface IssuePrepareRequest {
+
+ // Nonce to be used by the donau to derive
+ // its private inputs from. Must not have ever
+ // been used before.
+ nonce: CSNonce;
+
+ // Hash of the public key of the donation unit
+ // the request relates to.
+ donation_unit_pub_hash: HashCode;
+
+ }
+
+ .. ts:def:: IssuePrepareResponse
+
+ type IssuePrepareResponse =
+ | DonauIssueValue;
-All incoming `BDID` are recorded under the corresponding charity_id by the Donau.
+ .. ts:def:: DonauIssueValue
+
+ type DonauIssueValue =
+ | DonauRsaIssueValue
+ | DonauCsIssueValue;
+
+ .. ts:def:: DonauRsaIssueValue
+
+ interface DonauRsaIssueValue {
+ cipher: "RSA";
+ }
+
+ .. ts:def:: DonauCsIssueValue
+
+ interface DonauCsIssueValue {
+ cipher: "CS";
+
+ // CSR R0 value
+ r_pub_0: CsRPublic;
+
+ // CSR R1 value
+ r_pub_1: CsRPublic;
+ }
+
+
+Batch Issue
+~~~~~~~~~~~
+This is the effectiv issue receipts request. Depending on the amount of the donation a
+certain amount of blinded unique donation identifiers, or for short BUDIs, are required.
+Every BUDI will be signed by the corresponding requested donation unit, which is associated with a value.
+This API is used by the charity but the `BUDIKeyPairs` are coming from the donor.
+
+To make the request idempotent, the hash of the hole request is recorded under the
+corresponding charity_id by the Donau.
.. http:POST:: /batch-issue/$CHARITY_ID
- Send in a `IssueReceiptsRequest` and ask the Donau to sign all it's contained `BDID`.
+ Send in a `IssueReceiptsRequest` and ask the Donau to sign all it's contained BUDIs.
**Request:** `IssueReceiptsRequest`
**Response:**
:http:statuscode:`200 OK`:
- The request was successful, and the response is a `BSDonationReceipts`.
+ The request was successful, and the response is a `BlindedDonationReceiptSignatures`.
:http:statuscode:`403 Forbidden`:
The charity signature is invalid. This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
- At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`. This suggests a bug in the donor as it should have used current donation unit keys from :ref:`/keys<donau_status>`.
+ At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`.
+ This suggests a bug in the donor as it should have used current donation unit keys from :ref:`/keys<donau_status>`.
:http:statuscode:`409 Conflict`:
- The donation volume of the charity is not sufficient to issue donation receipts vor all sent in blinded udids. The response is a `IssueError` object.
+ The donation volume of the charity is not sufficient to issue donation receipts for all sent in blinded udis.
+ The response is a `IssueError` object.
:http:statuscode:`410 Gone`:
- The requested donation unit key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly.
+ The requested donation unit key is not yet or no longer valid. It either before the validity year, past the
+ year or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code
+ provided to understand which of the cases this is and handle it accordingly.
**Details:**
.. ts:def:: IssueReceiptsRequest
interface IssueReceiptsRequest {
- charity_signature: EddsaSignature;
+ charity_sig: EddsaSignature;
year: Integer;
- bdids: BDID[];
+ budikeypairs: BUDIKeyPairs[];
}
- .. ts:def:: BDID
+ .. ts:def:: BUDIKeyPairs
- interface BDID {
- donau_pub_hash: HashCode;
- taxpayer_blinded_id: BDIDEnvelope;
+ interface BUDIKeyPairs {
+ h_donation_unit_pub: HashCode;
+ blinded_udi: BUDI;
}
- .. ts:def:: BDIDEnvelope
+ .. ts:def:: BUDI
- type BDIDEnvelope = RSABDIDEnvelope | CSBDIDEnvelope ;
+ type BUDI = RSABUDI | CSBUDI ;
- .. ts:def:: RSABDIDEnvelope
+ .. ts:def:: RSABUDI
- interface RSABDIDEnvelope {
- cipher: "RSA" | "RSA+age_restricted";
- rsa_blinded_UDID: string; // Crockford Base32 encoded
+ interface RSABUDI {
+ cipher: "RSA";
+ rsa_blinded_udi: string; // Crockford Base32 encoded
}
- .. ts:def:: CSBDIDEnvelope
+ .. ts:def:: CSBUDI
- // For donation unit signatures based on Blind Clause-Schnorr, the UDID
+ // For donation unit signatures based on Blind Clause-Schnorr, the BUDI
// consists of the public nonce and two Curve25519 scalars which are two
// blinded challenges in the Blinded Clause-Schnorr signature scheme.
// See https://taler.net/papers/cs-thesis.pdf for details.
- interface CSBDIDEnvelope {
- cipher: "CS" | "CS+age_restricted";
+ interface CSBUDI {
+ cipher: "CS";
cs_nonce: string; // Crockford Base32 encoded
cs_blinded_c0: string; // Crockford Base32 encoded
cs_blinded_c1: string; // Crockford Base32 encoded
}
- .. ts:def:: BDIDBlindingKeyP
+ .. ts:def:: BUDIBlindingKeyP
// Secret for blinding/unblinding.
// An RSA blinding secret, which is basically
// a 256-bit nonce, converted to Crockford Base32.
- type BDIDBlindingKeyP = string;
+ type BUDIBlindingKeyP = string;
- .. ts:def:: BSDonationReceipts
+ .. ts:def:: BlindedDonationReceiptSignatures
- interface DonationReceipts {
- blind_signed_receipt_signatures: DonationReceiptSignature[];
+ interface BlindedDonationReceiptSignatures {
+ blind_signed_receipt_signatures: BlindedDonationReceiptSignature[];
}
- .. ts:def:: DonationReceiptSignature
-
.. ts:def:: BlindedDonationReceiptSignature
type BlindedDonationReceiptSignature =
@@ -359,25 +442,6 @@ All incoming `BDID` are recorded under the corresponding charity_id by the Donau
s: Cs25519Scalar;
}
-
- type DonationReceiptSignature = RSADonationReceiptSignature | CSDonationReceiptSignature ;
-
- .. ts:def:: RSADonationReceiptSignature
-
- interface RSADonationReceiptSignature {
- cipher: "RSA";
- rsa_blinded_donation_receipt_sig: string; // Crockford Base32 encoded
- }
-
- .. ts:def:: CSDonationReceiptSignature
-
- interface CSDonationReceiptSignature {
- cipher: "CS";
- cs_nonce: string; // Crockford Base32 encoded
- cs_blinded_c0: string; // Crockford Base32 encoded
- cs_blinded_c1: string; // Crockford Base32 encoded
- }
-
.. ts:def:: IssueError
interface IssueError{
@@ -401,30 +465,32 @@ All incoming `BDID` are recorded under the corresponding charity_id by the Donau
donau_sig: EddsaSignature;
}
-.. _donau_submit:
+.. _donation_statement:
----------------
-Submit receipts
----------------
+------------------
+Donation statement
+------------------
Inspired by the Taler exchange :ref:`Deposit<deposit-par>`.
+Donation statement operations are requested by a donor.
+
.. http:POST:: /submit
- Send in donation receipts for the past fiscal year, receive signed total back.
+ Send in donation receipts for the current or one of the past fiscal years.
+ The donor will reveive the signed total back, which is called the
+ donation statement.
**Request:** `SubmitDonationReceiptsRequest`
**Response:**
- :http:statuscode:`200 OK`:
- The request was successful, and the response is a `SubmitResponse`.
+ :http:statuscode:`201 Created`:
+ The request was successful, and the created `DonationStatement` is stored.
:http:statuscode:`403 Forbidden`:
One of the signatures is invalid. This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
At least one of the donation unit keys is not known to the Donau. Comes with a `DonationUnitUnknownError`.
- :http:statuscode:`410 Gone`:
- The requested donation unit key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a `DonationUnitExpiredMessage`. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly. FIXME: text does not match our use case well.
**Details:**
@@ -432,9 +498,9 @@ Inspired by the Taler exchange :ref:`Deposit<deposit-par>`.
interface SubmitDonationReceiptsRequest{
// hashed taxpayer ID plus salt
- taxnr_hashed: HashCode;
+ h_donor_tax_id: HashCode;
// All donation receipts must be for this year.
- year: Integer;
+ donation_year: Integer;
// Receipts should be sorted by amount.
donation_receipts: DonationReceipt[];
}
@@ -442,45 +508,59 @@ Inspired by the Taler exchange :ref:`Deposit<deposit-par>`.
.. ts:def:: DonationReceipt
interface DonationReceipt{
- donation_unit_pub_hash: HashCode;
+ h_donation_unit_pub: HashCode;
nonce: string;
- donau_sig: DonationSignature
+ donation_unit_sig: DonationReceiptSignature
}
- .. ts:def:: DonationSignature
+ .. ts:def:: DonationReceiptSignature
- type DonationSignature =
- RsaDonationSignature | CSDonationSignature;
+ type DonationReceiptSignature = RSADonationReceiptSignature | CSDonationReceiptSignature ;
- .. ts:def:: RsaDonationSignature
+ .. ts:def:: RSADonationReceiptSignature
- interface RsaDonationSignature {
+ interface RSADonationReceiptSignature {
cipher: "RSA";
// RSA signature
rsa_signature: RsaSignature;
}
- .. ts:def:: CSDonationSignature
+ .. ts:def:: CSDonationReceiptSignature
- interface CSDonationSignature {
+ interface CSDonationReceiptSignature {
type: "CS";
// R value component of the signature.
cs_signature_r: Cs25519Point;
// s value component of the signature.
- cs_signature_s: Cs25519Scalar:
+ cs_signature_s: Cs25519Scalar;
}
+.. http:GET:: /donation-statement/$YEAR/$HASH_DONOR_ID
+
+ Get the donation statement for a specific year and donor.
+ **Request:**
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The request was successful, and the response is a `DonationStatement`.
+ :http:statuscode:`403 Forbidden`:
+ One of the signatures is invalid. This response comes with a standard `ErrorDetail` response.
+ :http:statuscode:`404 Not found`:
+ Either the donor or the year or the corresponding donation statement was not found.
+ This response comes with a standard `ErrorDetail` response.
+
+ **Details:**
- .. ts:def:: SubmitResponse
+ .. ts:def:: DonationStatement
- interface SubmitResponse{
- // *accepted* total
+ interface DonationStatement {
total: Amount;
- // signature over taxid_hashed, total, year
+ // signature over h_donor_tax_id, total, donation_year
signature: EddsaSignature;
}
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
index 4c5be000..d48220fe 100644
--- a/core/api-exchange.rst
+++ b/core/api-exchange.rst
@@ -21,7 +21,7 @@ The Exchange RESTful API
The API specified here follows the :ref:`general conventions <http-common>`
for all details not specified in the individual requests.
-The `glossary <https://docs.taler.net/glossary.html#glossary>`_
+The `glossary <https://docs.taler.net/taler-developer-manual.html#developer-glossary>`_
defines all specific terms used in this section.
.. contents:: Table of Contents
@@ -62,7 +62,7 @@ possibly by using HTTPS.
as well as the list of possible KYC requirements. This endpoint is largely
for the SPA for AML officers. Merchants should use ``/keys`` which also
contains the protocol version and currency.
- This specification corresponds to ``current`` protocol being **v19**.
+ This specification corresponds to ``current`` protocol being **v20**.
**Response:**
@@ -104,7 +104,7 @@ possibly by using HTTPS.
// Code of the currency.
// Deprecated in protocol **v18** for the exchange
- // and in protocol v6 for the merchant.
+ // and in protocol **v6** for the merchant.
currency: string;
// how many digits the user may enter after the decimal_separator
@@ -193,7 +193,7 @@ possibly by using HTTPS.
// List of exchanges that this exchange is partnering
// with to enable wallet-to-wallet transfers.
- wads: ExchangePartner[];
+ wads: ExchangePartnerListEntry[];
// Set to true if this exchange allows the use
// of reserves for rewards.
@@ -443,7 +443,7 @@ possibly by using HTTPS.
.. ts:def:: DenomGroupCsAgeRestricted
- interface DenomGroupCSAgeRestricted extends DenomGroupCommon {
+ interface DenomGroupCsAgeRestricted extends DenomGroupCommon {
cipher: "CS+age_restricted";
age_mask: AgeMask;
@@ -655,9 +655,9 @@ possibly by using HTTPS.
sig: EddsaSignature;
}
- .. ts:def:: ExchangePartner
+ .. ts:def:: ExchangePartnerListEntry
- interface ExchangePartner {
+ interface ExchangePartnerListEntry {
// Base URL of the partner exchange.
partner_base_url: string;
@@ -776,7 +776,7 @@ Management operations authorized by master key
.. ts:def:: FutureSignKey
- interface SignKey {
+ interface FutureSignKey {
// The actual exchange's EdDSA signing public key.
key: EddsaPublicKey;
@@ -1272,7 +1272,7 @@ Management operations authorized by master key
**Request:**
- The request must be an `ExchangePartner` message.
+ The request must be an `ExchangePartnerSetupReqest` message.
**Response**
@@ -1285,9 +1285,9 @@ Management operations authorized by master key
**Details:**
- .. ts:def:: ExchangePartner
+ .. ts:def:: ExchangePartnerSetupRequest
- interface ExchangePartner {
+ interface ExchangePartnerSetupRequest {
// Base URL of the partner exchange
partner_base_url: string;
@@ -1323,19 +1323,191 @@ to allow exchange staff to monitor suspicious transactions
and freeze or unfreeze accounts suspected of money laundering.
-.. http:get:: /aml/$OFFICER_PUB/decisions/$STATE
+.. http:get:: /aml/$OFFICER_PUB/measures
- Obtain list of AML decisions (filtered by $STATE). ``$STATE`` must be either ``normal``, ``pending`` or ``frozen``.
+ To enable the AML staff SPA to give AML staff a choice of possible measures, a
+ new endpoint ``/aml/$OFFICER_PUB/measures`` is added that allows the AML SPA
+ to dynamically GET the list of available measures. It returns a list of known
+ KYC checks (by name) with their descriptions and a list of AML programs with
+ information about the required context.
- *Taler-AML-Officer-Signature*: The client must provide Base-32 encoded EdDSA signature with ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that this is merely a simple authentication mechanism, the details of the request are not protected by the signature.
+ This endpoint was introduced in protocol **v20**.
- :query delta: *Optional*. takes value of the form ``N (-N)``, so that at most ``N`` values strictly older (younger) than ``start`` are returned. Defaults to ``-20`` to return the last 20 entries (before ``start``).
- :query start: *Optional*. Row number threshold, see ``delta`` for its interpretation. Defaults to ``INT64_MAX``, namely the biggest row id possible in the database.
+ **Request:**
- **Response**
+ *Taler-AML-Officer-Signature*:
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that
+ this is merely a simple authentication mechanism, the details of the
+ request are not protected by the signature.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ Information about possible measures is returned in a
+ `AvailableMeasureSummary` object.
+
+ **Details:**
+
+ .. ts:def:: AvailableMeasureSummary
+
+ interface AvailableMeasureSummary {
+
+ // Available original measures that can be
+ // triggered directly by default rules.
+ roots: { "$measure_name" : MeasureInformation; };
+
+ // Available AML programs.
+ programs: { "$prog_name" : AmlProgramRequirement; };
+
+ // Available KYC checks.
+ checks: { "$check_name" : KycCheckInformation; };
+
+ }
+
+ .. ts:def:: MeasureInformation
+
+ interface MeasureInformation {
+
+ // Name of a KYC check.
+ check_name: string;
+
+ // Name of an AML program.
+ prog_name: string;
+
+ // Context for the check. Optional.
+ context?: Object;
+
+ }
+
+ .. ts:def:: AmlProgramRequirement
+
+ interface AmlProgramRequirement {
+
+ // Description of what the AML program does.
+ description: string;
+
+ // List of required field names in the context to run this
+ // AML program. SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using this program.
+ context: string[];
+
+ // List of required attribute names in the
+ // input of this AML program. These attributes
+ // are the minimum that the check must produce
+ // (it may produce more).
+ inputs: string[];
+
+ }
+
+ .. ts:def:: KycCheckInformation
+
+ interface KycCheckInformation {
+
+ // Description of the KYC check. Should be shown
+ // to the AML staff but will also be shown to the
+ // client when they initiate the check in the KYC SPA.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n: { [lang_tag: string]: string};
+
+ // Names of the fields that the CONTEXT must provide
+ // as inputs to this check.
+ // SPA must check that the AML staff is providing
+ // adequate CONTEXT when defining a measure using
+ // this check.
+ requires: string[];
+
+ // Names of the attributes the check will output.
+ // SPA must check that the outputs match the
+ // required inputs when combining a KYC check
+ // with an AML program into a measure.
+ outputs: string[];
+
+ // Name of a root measure taken when this check fails.
+ fallback: string;
+ }
+
+.. http:get:: /aml/$OFFICER_PUB/kyc-statistics/$NAME
+
+ Returns the number of KYC events matching the given event type ``$NAME`` in
+ the specified time range. Note that this query can be slow as the
+ statistics are computed on-demand. (This is OK as such requests should be
+ rare.)
+
+ This endpoint was introduced in protocol **v20**.
+
+ **Request:**
+
+ *Taler-AML-Officer-Signature*:
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that this
+ is merely a simple authentication mechanism, the details of the request are
+ not protected by the signature.
+
+ :query start_date=TIMESTAMP:
+ *Optional*. Specifies the date when to
+ start looking (inclusive). If not given, the start time of the
+ exchange operation is used.
+ :query end_date=TIMESTAMP:
+ *Optional*. Specifies the date when to
+ stop looking (exclusive). If not given, the current date is used.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The responds will be an `EventCounter` message.
+
+ **Details:**
+
+ .. ts:def:: EventCounter
+
+ interface EventCounter {
+ // Number of events of the specified type in
+ // the given range.
+ counter: Integer;
+ }
+
+
+.. http:get:: /aml/$OFFICER_PUB/decisions
+
+ **Request:**
+
+ *Taler-AML-Officer-Signature*:
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that
+ this is merely a simple authentication mechanism, the details of the
+ request are not protected by the signature.
+
+ This endpoint was introduced in this form in protocol **v20**.
+
+ :query limit:
+ *Optional*. takes value of the form ``N (-N)``, so that at
+ most ``N`` values strictly older (younger) than ``start`` are returned.
+ Defaults to ``-20`` to return the last 20 entries (before ``start``).
+ :query offset:
+ *Optional*. Row number threshold, see ``delta`` for its
+ interpretation. Defaults to ``INT64_MAX``, namely the biggest row id
+ possible in the database.
+ :query h_payto:
+ *Optional*. Account selector. All matching accounts are returned if this
+ filter is absent, otherwise only decisions for this account.
+ :query active:
+ *Optional*. If set to yes, only return active decisions, if no only
+ decisions that have been superceeded. Do not give (or use "all") to
+ see all decisions regardless of activity status.
+ :query investigation:
+ *Optional*. If set to yes, only return accounts that are under
+ AML investigation, if no only accounts that are not under investigation.
+ Do not give (or use "all") to see all accounts regardless of
+ investigation status.
+
+ **Response:**
:http:statuscode:`200 OK`:
- The responds will be an `AmlRecords` message.
+ The response will be an `AmlDecisionsResponse` message.
:http:statuscode:`204 No content`:
There are no matching AML records.
:http:statuscode:`403 Forbidden`:
@@ -1347,100 +1519,217 @@ and freeze or unfreeze accounts suspected of money laundering.
**Details:**
- .. ts:def:: AmlRecords
+ .. ts:def:: AmlDecisionsResponse
- interface AmlRecords {
+ interface AmlDecisionsResponse {
- // Array of AML records matching the query.
- records: AmlRecord[];
+ // Array of AML decisions matching the query.
+ records: AmlDecision[];
}
- .. ts:def:: AmlRecord
+ .. ts:def:: AmlDecision
- interface AmlRecord {
+ interface AmlDecision {
// Which payto-address is this record about.
// Identifies a GNU Taler wallet or an affected bank account.
h_payto: PaytoHash;
- // What is the current AML state.
- current_state: Integer;
+ // Row ID of the record. Used to filter by offset.
+ rowid: Integer;
- // Monthly transaction threshold before a review will be triggered
- threshold: Amount;
+ // Justification for the decision.
+ justification: string;
- // RowID of the record.
- rowid: Integer;
+ // When was the decision made?
+ decision_time: Timestamp;
+
+ // Free-form properties about the account.
+ // Can be used to store properties such as PEP,
+ // risk category, type of business, hits on
+ // sanctions lists, etc.
+ properties?: AccountProperties;
+
+ // What are the new rules?
+ limits: LegitimizationRuleSet;
+
+ // True if the account is under investigation by AML staff
+ // after this decision.
+ to_investigate: boolean;
+
+ // True if this is the active decision for the
+ // account.
+ is_active: boolean;
}
+ .. ts:def:: AccountProperties
-.. http:get:: /aml/$OFFICER_PUB/decision/$H_PAYTO
+ // All fields in this object are optional. The actual
+ // properties collected depend fully on the discretion
+ // of the exchange operator;
+ // however, some common fields are standardized
+ // and thus described here.
+ interface AccountProperties {
- Obtain deails about an AML decision.
+ // True if this is a politically exposed account.
+ // Rules for classifying accounts as politically
+ // exposed are country-dependent.
+ pep?: boolean;
- *Taler-AML-Officer-Signature*: The client must provide Base-32 encoded EdDSA signature with ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that this is merely a simple authentication mechanism, the details of the request are not protected by the signature.
+ // True if this is a sanctioned account.
+ // Rules for classifying accounts as sanctioned
+ // are country-dependent.
+ sanctioned?: boolean;
- :query history: *Optional*. If set to yes, we return all historic decisions and not only the last one.
+ // True if this is a high-risk account.
+ // Rules for classifying accounts as at-risk
+ // are exchange operator-dependent.
+ high_risk?: boolean;
- **Response**
+ // Business domain of the account owner.
+ // The list of possible business domains is
+ // operator- or country-dependent.
+ business_domain?: string;
- :http:statuscode:`200 OK`:
- The responds will be an `AmlDecisionDetails` message.
- :http:statuscode:`204 No content`:
- There are no matching AML records for the given payto://-URI.
- :http:statuscode:`403 Forbidden`:
- The signature is invalid.
- :http:statuscode:`404 Not found`:
- The designated AML account is not known.
- :http:statuscode:`409 Conflict`:
- The designated AML account is not enabled.
+ // Is the client's account currently frozen?
+ is_frozen?: boolean;
- **Details:**
+ // Was the client's account reported to the authorities?
+ was_reported?: boolean;
+
+ }
+
+ .. ts:def:: LegitimizationRuleSet
+
+ interface LegitimizationRuleSet {
+
+ // When does this set of rules expire and
+ // we automatically transition to the successor
+ // measure?
+ expiration_time: Timestamp;
- .. ts:def:: AmlDecisionDetails
+ // Name of the measure to apply when the expiration time is
+ // reached. If not set, we refer to the default
+ // set of rules (and the default account state).
+ successor_measure?: string;
- interface AmlDecisionDetails {
+ // Legitimization rules that are to be applied
+ // to this account.
+ rules: KycRule[];
- // Array of AML decisions made for this account. Possibly
- // contains only the most recent decision if "history" was
- // not set to 'true'.
- aml_history: AmlDecisionDetail[];
+ // Custom measures that KYC rules and the
+ // ``successor_measure`` may refer to.
+ custom_measures: { "$measure_name" : MeasureInformation; };
- // Array of KYC attributes obtained for this account.
- kyc_attributes: KycDetail[];
}
- .. ts:def:: AmlDecisionDetail
+ .. ts:def:: KycRule
- interface AmlDecisionDetail {
+ interface KycRule {
- // What was the justification given?
- justification: string;
+ // Type of operation to which the rule applies.
+ operation_type: string;
+
+ // The measures will be taken if the given
+ // threshold is crossed over the given timeframe.
+ threshold: Amount;
- // FIXME: review!
- // What is the new AML state.
- new_state: Integer;
+ // Over which duration should the ``threshold`` be
+ // computed. All amounts of the respective
+ // ``operation_type`` will be added up for this
+ // duration and the sum compared to the ``threshold``.
+ timeframe: RelativeTime;
- // When was this decision made?
- decision_time: Timestamp;
+ // Array of names of measures to apply.
+ // Names listed can be original measures or
+ // custom measures from the `AmlOutcome`.
+ // A special measure "verboten" is used if the
+ // threshold may never be crossed.
+ measures: string[];
+
+ // If multiple rules apply to the same account
+ // at the same time, the number with the highest
+ // rule determines which set of measures will
+ // be activated and thus become visible for the
+ // user.
+ display_priority: Integer;
+
+ // True if the rule (specifically, operation_type,
+ // threshold, timeframe) and the general nature of
+ // the measures (verboten or approval required)
+ // should be exposed to the client.
+ // Defaults to "false" if not set.
+ exposed?: boolean;
+
+ // True if all the measures will eventually need to
+ // be satisfied, false if any of the measures should
+ // do. Primarily used by the SPA to indicate how
+ // the measures apply when showing them to the user;
+ // in the end, AML programs will decide after each
+ // measure what to do next.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean;
+
+ }
+
+.. http:get:: /aml/$OFFICER_PUB/attributes/$H_PAYTO
+
+ Obtain attributes obtained as part of AML/KYC processes for a
+ given account.
+
+ This endpoint was introduced in protocol **v20**.
- // What is the new AML decision threshold (in monthly transaction volume)?
- new_threshold: Amount;
+ **Request:**
+
+ *Taler-AML-Officer-Signature*:
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$OFFICER_PRIV``, affirming the desire to obtain AML data. Note that
+ this is merely a simple authentication mechanism, the details of the
+ request are not protected by the signature.
+
+ :query limit:
+ *Optional*. takes value of the form ``N (-N)``, so that at
+ most ``N`` values strictly older (younger) than ``start`` are returned.
+ Defaults to ``-20`` to return the last 20 entries (before ``start``).
+ :query offset:
+ *Optional*. Row number threshold, see ``delta`` for its
+ interpretation. Defaults to ``INT64_MAX``, namely the biggest row id
+ possible in the database.
- // Who made the decision?
- decider_pub: AmlOfficerPublicKeyP;
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The responds will be an `KycAttributes` message.
+ :http:statuscode:`204 No content`:
+ There are no matching KYC attributes.
+ :http:statuscode:`403 Forbidden`:
+ The signature is invalid.
+ :http:statuscode:`404 Not found`:
+ The designated AML account is not known.
+ :http:statuscode:`409 Conflict`:
+ The designated AML account is not enabled.
+
+ .. ts:def:: KycAttributes
+
+ interface KycAttributes {
+
+ // Matching KYC attribute history of the account.
+ details: KycAttributeCollectionEvent[];
}
- .. ts:def:: KycDetail
+ .. ts:def:: KycAttributeCollectionEvent
- interface KycDetail {
+ interface KycAttributeCollectionEvent {
+
+ // Row ID of the record. Used to filter by offset.
+ rowid: Integer;
- // Name of the configuration section that specifies the provider
- // which was used to collect the KYC details
- // FIXME: review!
- provider_section: string;
+ // Name of the provider
+ // which was used to collect the attributes. NULL if they were
+ // just uploaded via a form by the account owner.
+ provider_name?: string;
// The collected KYC data. NULL if the attribute data could not
// be decrypted (internal error of the exchange, likely the
@@ -1450,9 +1739,6 @@ and freeze or unfreeze accounts suspected of money laundering.
// Time when the KYC data was collected
collection_time: Timestamp;
- // Time when the validity of the KYC data will expire
- expiration_time: Timestamp;
-
}
@@ -1463,7 +1749,7 @@ and freeze or unfreeze accounts suspected of money laundering.
**Request:**
- The request must be an `AmlDecision` message.
+ The request must be an `AmlDecisionRequest` message.
**Response**
@@ -1480,24 +1766,24 @@ and freeze or unfreeze accounts suspected of money laundering.
**Details:**
- .. ts:def:: AmlDecision
+ .. ts:def:: AmlDecisionRequest
- interface AmlDecision {
+ interface AmlDecisionRequest {
// Human-readable justification for the decision.
justification: string;
- // At what monthly transaction volume should the
- // decision be automatically reviewed?
- new_threshold: Amount;
-
// Which payto-address is the decision about?
// Identifies a GNU Taler wallet or an affected bank account.
h_payto: PaytoHash;
- // What is the new AML state (e.g. frozen, unfrozen, etc.)
- // Numerical values are defined in `AmlDecisionState`.
- new_state: Integer;
+ // What are the new rules?
+ // New since protocol **v20**.
+ new_rules: LegitimizationRuleSet;
+
+ // True if the account should remain under investigation by AML staff.
+ // New since protocol **v20**.
+ keep_investigating: boolean;
// Signature by the AML officer over a `TALER_AmlDecisionPS`.
// Must have purpose ``TALER_SIGNATURE_MASTER_AML_KEY``.
@@ -1506,9 +1792,6 @@ and freeze or unfreeze accounts suspected of money laundering.
// When was the decision made?
decision_time: Timestamp;
- // Optional argument to impose new KYC requirements
- // that the customer has to satisfy to unblock transactions.
- kyc_requirements?: string[];
}
@@ -1769,7 +2052,7 @@ Batch Withdraw
The user should be redirected to the provided location to perform
the required KYC checks to open the account before withdrawing.
Afterwards, the request should be repeated.
- The response will be an `KycNeededRedirect` object.
+ The response will be an `LegitimizationNeededResponse` object.
Implementation note: internally, we need to
distinguish between upgrading the reserve to an
@@ -1857,29 +2140,6 @@ Batch Withdraw
}
- .. ts:def:: KycNeededRedirect
-
- interface KycNeededRedirect {
-
- // Numeric `error code <error-codes>` unique to the condition.
- // Should always be ``TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED``.
- code: number;
-
- // Human-readable description of the error, i.e. "missing parameter", "commitment violation", ...
- // Should give a human-readable hint about the error's nature. Optional, may change without notice!
- hint?: string;
-
- // Hash of the payto:// account URI that identifies
- // the account which is being KYCed.
- h_payto: PaytoHash;
-
- // Legitimization target that the merchant should
- // use to check for its KYC status using
- // the ``/kyc-check/$REQUIREMENT_ROW/...`` endpoint.
- requirement_row: Integer;
-
- }
-
.. ts:def:: WithdrawError
interface WithdrawError {
@@ -1986,7 +2246,7 @@ If so, the exchange will blindly sign ``n`` undisclosed coins from the request.
The user should be redirected to the provided location to perform
the required KYC checks to open the account before withdrawing.
Afterwards, the request should be repeated.
- The response will be an `KycNeededRedirect` object.
+ The response will be an `LegitimizationNeededResponse` object.
.. ts:def:: AgeWithdrawRequest
@@ -2398,30 +2658,6 @@ Reserve History
request_timestamp: Timestamp;
}
- .. ts:def:: ReserveCreditTransaction
-
- interface ReserveCreditTransaction {
- type: "CREDIT";
-
- // Offset of this entry in the reserve history.
- // Useful to request incremental histories via
- // the "start" query parameter.
- history_offset: Integer;
-
- // Amount deposited.
- amount: Amount;
-
- // Sender account ``payto://`` URL.
- sender_account_url: string;
-
- // Opaque identifier internal to the exchange that
- // uniquely identifies the wire transfer that credited the reserve.
- wire_reference: Integer;
-
- // Timestamp of the incoming wire transfer.
- timestamp: Timestamp;
- }
-
.. ts:def:: PurseMergeTransaction
interface PurseMergeTransaction {
@@ -3046,7 +3282,7 @@ proof to the seller for the escrow of sufficient fund.
cs_signature_r: Cs25519Point;
// s value component of the signature.
- cs_signature_s: Cs25519Scalar:
+ cs_signature_s: Cs25519Scalar;
}
@@ -3287,7 +3523,7 @@ the API during normal operation.
.. ts:def:: MeltPrepareRequest
- interface WithdrawPrepareRequest {
+ interface MeltPrepareRequest {
// Master seed for the Clause-schnorr R-value
// creation.
@@ -3970,6 +4206,7 @@ typically also view the balance.)
// Current AML state for the target account. Non-zero
// values indicate that the transfer is blocked due to
// AML enforcement.
+ // Removed in protocol **v20**.
aml_decision: Integer;
// True if the KYC check for the merchant has been
@@ -4131,9 +4368,6 @@ Wallet-to-wallet transfers
// purse will (have been) merged with the account.
balance: Amount;
- // When does the purge expire.
- purse_expiration: Timestamp;
-
// Time of the merge, missing if "never".
merge_timestamp?: Timestamp;
@@ -4178,7 +4412,7 @@ Wallet-to-wallet transfers
:http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that all
coins were deposited into the purse.
- The response will include a `PurseDepositSuccess` object.
+ The response will include a `PurseCreateSuccessResponse` object.
:http:statuscode:`403 Forbidden`:
A coin, denomination or contract signature is invalid.
This response comes with a standard `ErrorDetail` response.
@@ -4266,40 +4500,9 @@ Wallet-to-wallet transfers
}
- .. ts:def:: PurseDeposit
-
- interface PurseDeposit {
+ .. ts:def:: PurseCreateSuccessResponse
- // Amount to be deposited, can be a fraction of the
- // coin's total value.
- amount: Amount;
-
- // Hash of denomination RSA key with which the coin is signed.
- denom_pub_hash: HashCode;
-
- // Exchange's unblinded RSA signature of the coin.
- ub_sig: DenominationSignature;
-
- // Age commitment for the coin, if the denomination is age-restricted.
- age_commitment?: AgeCommitment;
-
- // Attestation for the minimum age, if the denomination is age-restricted.
- attest?: Attestation;
-
- // Signature over `TALER_PurseDepositSignaturePS`
- // of purpose ``TALER_SIGNATURE_WALLET_PURSE_DEPOSIT``
- // made by the customer with the
- // `coin's private key <coin-priv>`.
- coin_sig: EddsaSignature;
-
- // Public key of the coin being deposited into the purse.
- coin_pub: EddsaPublicKey;
-
- }
-
- .. ts:def:: PurseDepositSuccess
-
- interface PurseDepositSuccess {
+ interface PurseCreateSuccessResponse {
// Total amount deposited into the purse so far (without fees).
total_deposited: Amount;
@@ -4465,7 +4668,7 @@ Wallet-to-wallet transfers
:http:statuscode:`451 Unavailable For Legal Reasons`:
This account has not yet passed the KYC checks.
The client must pass KYC checks before proceeding with the merge.
- The response will be an `KycNeededRedirect` object.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
@@ -4550,7 +4753,7 @@ Wallet-to-wallet transfers
:http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that the
purse was allocated.
- The response will include a `PurseDepositSuccess` object.
+ The response will include a `PurseCreateSuccessResponse` object.
:http:statuscode:`402 Payment Required`:
The account needs to contain more funding to create more purses.
This response comes with a standard `ErrorDetail` response.
@@ -4576,7 +4779,7 @@ Wallet-to-wallet transfers
:http:statuscode:`451 Unavailable For Legal Reasons`:
This account has not yet passed the KYC checks.
The client must pass KYC checks before proceeding with the merge.
- The response will be an `KycNeededRedirect` object.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
@@ -4652,7 +4855,7 @@ Wallet-to-wallet transfers
:http:statuscode:`200 OK`:
The operation succeeded, the exchange confirms that all
coins were deposited into the purse.
- The response will include a `PurseDepositSuccess` object.
+ The response will include a `PurseDepositSuccessResponse` object.
:http:statuscode:`403 Forbidden`:
A coin or denomination signature is invalid.
This response comes with a standard `ErrorDetail` response.
@@ -4714,9 +4917,9 @@ Wallet-to-wallet transfers
}
- .. ts:def:: PurseDepositSuccess
+ .. ts:def:: PurseDepositSuccessResponse
- interface PurseDepositSuccess {
+ interface PurseDepositSuccessResponse {
// Total amount paid into the purse.
total_deposited: Amount;
@@ -4869,6 +5072,17 @@ regulatory compliance.
.. http:post:: /kyc-wallet
+ The ``/wallet-kyc`` POST endpoint allows a wallet to notify an exchange if
+ it will cross a balance threshold. Here, the ``balance`` specified should be
+ the threshold (from the ``wallet_balance_limit_without_kyc`` array) that the
+ wallet would cross, and *not* the *exact* balance of the wallet. The exchange
+ will respond with a wire target UUID. The wallet can then use this UUID to
+ being the KYC process at ``/kyc-check/``. The wallet must only proceed to
+ obtain funds exceeding the threshold after the KYC process has concluded.
+ While wallets could be "hacked" to bypass this measure (we cannot
+ cryptographically enforce this), such modifications are a terms of service
+ violation which may have legal consequences for the user.
+
Setup KYC identification for a wallet. Returns the KYC UUID.
This endpoint is used by compliant Taler wallets when they
are about to hit the balance threshold and thus need to have
@@ -4892,7 +5106,7 @@ regulatory compliance.
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`451 Unavailable for Legal Reasons`:
The wallet must undergo a KYC check. A KYC ID was created.
- The response will be a `WalletKycUuid` object.
+ The response will be a `LegitimizationNeededResponse` object (changed in protocol **v20**).
**Details:**
@@ -4915,73 +5129,76 @@ regulatory compliance.
reserve_pub: EddsaPublicKey;
}
- .. ts:def:: WalletKycUuid
+.. http:get:: /kyc-check/$REQUIREMENT_ROW
- interface WalletKycUuid {
+ Checks the KYC status of a particular payment target and possibly begins a
+ KYC process by allowing the customer to choose the next KYC measure to
+ satisfy. This endpoint is typically used by wallets or merchants that
+ have been told that a transaction is not happening because it triggered
+ some KYC/AML measure and now want to check how the KYC/AML
+ requirement could be fulfilled (or whether it already has been
+ statisfied and the operation can now proceed). Long-polling may be used
+ to instantly observe a change in the KYC requirement status.
- // UUID that the wallet should use when initiating
- // the KYC check.
- requirement_row: number;
+ The requirement row of the ``/kyc-check/`` endpoint encodes the
+ legitimization measure's serial number. It is returned in
+ `LegitimizationNeededResponse` responses via the ``requirement_row`` field.
- // Hash of the payto:// account URI for the wallet.
- h_payto: PaytoHash;
+ Given a valid pair of requirement row and account owner signature, the
+ ``/kyc-check/`` endpoint returns either just the KYC status or redirects the
+ client (202) to the next required stage of the KYC process. The redirection
+ must be for an HTTP(S) endpoint to be triggered via a simple HTTP GET. It
+ must always be the same endpoint for the same client, as the wallet/merchant
+ backend are not required to check for changes to this endpoint. Clients
+ that received a 202 status code may repeat the request and use long-polling
+ to detect a change of the HTTP status.
- }
+ This endpoint exists in this specific form only since protocol **v20**.
+ **Request:**
-.. http:get:: /kyc-check/$REQUIREMENT_ROW/$H_PAYTO/$USERTYPE
-
- Checks the KYC status of a particular payment target and possibly begins the
- KYC process. This endpoint is used by wallets or merchants that have been
- told about a KYC requirement and now want to check if the KYC requirement
- has been fulfilled. Long-polling may be used to instantly observe a change
- in the KYC requirement status.
-
- Returns the current KYC status of the requirement process and, if negative,
- returns the URL where the KYC process can be initiated. The
- ``$REQUIREMENT_ROW`` must have been returned previously from an exchange API
- endpoint that determined that KYC was needed. The ``$H_PATYO`` must be the
- hash of the "payto://" URI of the payment target. The ``$USERTYPE`` states
- whether the entity to perform the KYC is an "individual" or a "business".
+ *Account-Owner-Signature*:
- **Request:**
+ The client must provide Base-32 encoded EdDSA signature with
+ ``$ACCOUNT_PRIV``, affirming the desire to obtain KYC data. Note that
+ this is merely a simple authentication mechanism, the details of the
+ request are not protected by the signature. The ``$ACCOUNT_PRIV`` is
+ either the (wallet long-term) reserve private key or the merchant instance
+ private key.
:query timeout_ms=NUMBER: *Optional.* If specified, the exchange will
- wait up to ``timeout_ms`` milliseconds if the payment target
- is currently not legitimized. Ignored if the payment target
- is already legitimized. Note that the legitimization would be
- triggered by another request to the same endpoint with a valid
- ``token``.
+ wait up to ``timeout_ms`` milliseconds if the requirement continues
+ to be mandatory provisioning of KYC data by the client.
+ Ignored if the HTTP status code is already ``200 Ok``. Note that
+ clients cannot long-poll for AML staff actions, so status information
+ about an account being under AML review needs to be requested
+ periodically.
**Response:**
:http:statuscode:`200 Ok`:
- The KYC operation succeeded, the exchange confirms that the
- payment target already authorized to transact.
- The response will be an `AccountKycStatus` object.
+ No mandatory KYC actions are required by the client at this time.
+ The client *may* still visit the KYC URL to initiate voluntary checks.
+ The response will be an `AccountKycStatus` object which specifies
+ restrictions that currently apply to the account. If the
+ client attempts to exceed *soft* limits, the status may change
+ to a ``202 Accepted``. Hard limits cannot be lifted by passing KYC checks.
:http:statuscode:`202 Accepted`:
- The user should be redirected to the provided location to perform
+ The account holder performed an operation that would have crossed
+ *soft* limits and must be redirected to the provided location to perform
the required KYC checks to satisfy the legal requirements. Afterwards, the
``/kyc-check/`` request should be repeated to check whether the
user has completed the process.
- The response will be an `AccountKycRedirect` object.
+ The response will be an `AccountKycStatus` object.
:http:statuscode:`204 No content`:
The exchange is not configured to perform KYC and thus
the legal requirements are already satisfied.
- :http:statuscode:`402 Payment Required`:
- The client must pay the KYC fee for the KYC process.
- **This is currently not implemented, see #7365.**
:http:statuscode:`403 Forbidden`:
- The provided hash does not match the payment target.
+ The provided signature is not acceptable for the requirement row. The (base32) encoding of the public key we expect signatures to be made with is returned in the ``detail`` field.
:http:statuscode:`404 Not found`:
- The payment target is unknown.
- :http:statuscode:`451 Unavailable for Legal Reasons`:
- The transaction cannot be completed due to AML rules.
- Thus, the operation is currently not stuck on KYC, but
- on exchange staff performing their AML review. The user
- should be told to wait and/or contact the exchange operator
- if the situation persists.
- The response will be a `AccountAmlBlocked` object.
+ The requirement row is unknown.
+ :http:statuscode:`409 Conflict`:
+ The bank account is not (yet) associated with a public key. The account owner must do a wire transfer to the exchange with the account public key in the wire transfer subject to enable authentication.
**Details:**
@@ -4989,75 +5206,274 @@ regulatory compliance.
interface AccountKycStatus {
- // Details about the KYC check that the user
- // passed.
- kyc_details: KycDetails;
+ // Current AML state for the target account. True if
+ // operations are not happening due to staff processing
+ // paperwork *or* due to legal requirements (so the
+ // client cannot do anything but wait).
+ //
+ // Note that not every AML staff action may be legally
+ // exposed to the client, so this is merely a hint that
+ // a client should be told that AML staff is currently
+ // reviewing the account. AML staff *may* review
+ // accounts without this flag being set!
+ aml_review: boolean;
+
+ // Access token needed to construct the ``/kyc-spa/``
+ // URL that the user should open in a browser to
+ // proceed with the KYC process (optional if the status
+ // type is ``200 Ok``, mandatory if the HTTP status
+ // is ``202 Accepted``).
+ access_token: AccountAccessToken;
+
+ // Array with limitations that currently apply to this
+ // account and that may be increased or lifted if the
+ // KYC check is passed.
+ // Note that additional limits *may* exist and not be
+ // communicated to the client. If such limits are
+ // reached, this *may* be indicated by the account
+ // going into ``aml_review`` state. However, it is
+ // also possible that the exchange may legally have
+ // to deny operations without being allowed to provide
+ // any justification.
+ // The limits should be used by the client to
+ // possibly structure their operations (e.g. withdraw
+ // what is possible below the limit, ask the user to
+ // pass KYC checks or withdraw the rest after the time
+ // limit is passed, warn the user to not withdraw too
+ // much or even prevent the user from generating a
+ // request that would cause it to exceed hard limits).
+ limits?: AccountLimit[];
+
+ }
+
+ .. ts:def:: AccountLimit
+
+ interface AccountLimit {
+
+ // Operation that is limited.
+ // Must be one of "WITHDRAW", "DEPOSIT", "P2P-RECEIVE"
+ // or "WALLET-BALANCE".
+ operation_type: string;
+
+ // Timeframe during which the limit applies.
+ timeframe: RelativeTime;
+
+ // Maximum amount allowed during the given timeframe.
+ // Zero if the operation is simply forbidden.
+ threshold: Amount;
- // Current time of the exchange, used as part of
- // what the exchange signs over.
- now: Timestamp;
+ // True if this is a soft limit that could be raised
+ // by passing KYC checks. Clients *may* deliberately
+ // try to cross limits and trigger measures resulting
+ // in 451 responses to begin KYC processes.
+ // Clients that are aware of hard limits *should*
+ // inform users about the hard limit and prevent flows
+ // in the UI that would cause violations of hard limits.
+ soft_limit: boolean;
+ }
+
+.. http:get:: /kyc-spa/$ACCESS_TOKEN
+.. http:get:: /kyc-spa/$FILENAME
+
+ A set of ``/kyc-spa/$ACCESS_TOKEN`` GET endpoints is created per account
+ hash that serves the KYC SPA. This is where the ``/kyc-check/`` endpoint
+ will in principle redirect clients. The KYC SPA will use the
+ ``$ACCESS_TOKEN`` of its URL to initialize itself via the
+ ``/kyc-info/$ACCESS_TOKEN`` endpoint family. The KYC SPA may download
+ additional resources via ``/kyc-spa/$FILENAME``. The filenames must not
+ match base32-encoded 256-bit values.
+
+ This endpoint was introduced in protocol **v20**.
+
+
+.. http:get:: /kyc-info/$ACCESS_TOKEN
+
+ The ``/kyc-info/$ACCESS_TOKEN`` endpoints are created per client
+ account hash (but access controlled via a unique target token)
+ to return information about the state of the KYC or AML process
+ to the KYC SPA. The SPA uses this information to show the user an
+ appropriate dialog. The SPA should also long-poll this endpoint for changes
+ to the AML/KYC state. Note that this is a client-facing endpoint, so it will
+ only provide a restricted amount of information to the customer (as some
+ laws may forbid us to inform particular customers about their true status).
+ The endpoint will typically inform the SPA about possible choices to
+ proceed, such as directly uploading files, contacting AML staff, or
+ proceeding with a particular KYC process at an external provider (such as
+ Challenger). If the user chooses to initate a KYC process at an external
+ provider, the SPA must request the respective process to be set-up by the
+ exchange via the ``/kyc-start/`` endpoint.
+
+ This endpoint was introduced in protocol **v20**.
- // EdDSA signature of the exchange affirming the account
- // is KYC'ed, must be of purpose
- // ``TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS``
- // and over ``TALER_AccountSetupStatusSignaturePS``.
- exchange_sig: EddsaSignature;
+ **Request:**
- // public key used to create the signature.
- exchange_pub: EddsaPublicKey;
+ *If-None-Match*:
+ The client MAY provide an ``If-None-Match`` header with an ETag.
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- aml_status: Integer;
+ :query timeout_ms=MILLISECONDS:
+ *Optional.* If specified, the exchange will wait up to MILLISECONDS for
+ a change to a more recent legitimization measure before returning a 304
+ Not Modified status.
+
+ **Response:**
+ *Etag*: Will be set to the serial ID of the measure. Used for long-polling (only for 200 OK responses).
+
+ :http:statuscode:`200 OK`:
+ The body is a `KycProcessClientInformation`.
+ :http:statuscode:`204 No Content`:
+ There are no open KYC requirements or possible voluntary checks
+ the client might perform.
+ :http:statuscode:`304 Not Modified`:
+ The KYC requirements did not change.
+
+
+ **Details:**
+
+ .. ts:def:: KycProcessClientInformation
+
+ interface KycProcessClientInformation {
+
+ // List of requirements.
+ requirements?: { name : KycRequirementInformation};
+
+ // True if the client is expected to eventually satisfy all requirements.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean
+
+ // List of available voluntary checks the client could pay for.
+ // Since **vATTEST**.
+ voluntary_checks?: { name : KycCheckPublicInformation};
}
- .. ts:def:: AccountKycRedirect
+ .. ts:def:: KycRequirementInformation
- interface AccountKycRedirect {
+ interface KycRequirementInformation {
- // URL that the user should open in a browser to
- // proceed with the KYC process.
- kyc_url: string;
+ // Which form should be used? Common values include "INFO"
+ // (to just show the descriptions but allow no action),
+ // "LINK" (to enable the user to obtain a link via
+ // ``/kyc-start/``) or any build-in form name supported
+ // by the SPA.
+ form: string;
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- aml_status: Integer;
+ // English description of the requirement.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n ?: { [lang_tag: string]: string };
+
+ // ID of the requirement, useful to construct the
+ // ``/kyc-upload/$ID`` or ``/kyc-start/$ID`` endpoint URLs.
+ // Present if and only if "form" is not "INFO". The
+ // ``$ID`` value may itself contain ``/`` or ``?`` and
+ // basically encode any URL path (and optional arguments).
+ id?: string;
}
- .. ts:def:: AccountAmlBlocked
+ .. ts:def:: KycCheckPublicInformation
- interface AccountAmlBlocked {
+ // Since **vATTEST**.
+ interface KycCheckPublicInformation {
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- aml_status: Integer;
+ // English description of the check.
+ description: string;
+ // Map from IETF BCP 47 language tags to localized
+ // description texts.
+ description_i18n ?: { [lang_tag: string]: string };
+
+ // FIXME: is the above in any way sufficient
+ // to begin the check? Do we not need at least
+ // something more??!?
}
- .. ts:def:: KycDetails
- // Object that specifies which KYC checks are satisfied.
- interface KycDetails {
+.. http:post:: /kyc-upload/$ID
+
+ The ``/kyc-upload/$ID`` POST endpoint allows the SPA to upload
+ client-provided evidence. The ``$ID`` will be provided as part of the
+ ``/kyc-info`` body. This is for checks of type ``FORM``. In practice,
+ ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of the selected
+ measure (but this should be irrelevant for the client).
+
+ This endpoint was introduced in protocol **v20**.
+
+ **Request:**
+
+ Basically oriented along the possible formats of a HTTP form being
+ POSTed. Details will depend on the form. The server will try to decode the
+ uploaded body from whatever format it is provided in.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The information was successfully uploaded. The SPA should fetch
+ an updated ``/kyc-info/``.
+ :http:statuscode:`404 Not Found`:
+ The ``$ID`` is unknown to the exchange.
+ :http:statuscode:`409 Conflict`:
+ The upload conflicts with a previous upload.
+ :http:statuscode:`413 Request Entity Too Large`:
+ The body is too large.
+
+.. http:post:: /kyc-start/$ID
+
+ The ``/kyc-start/$ID`` POST endpoint allows the SPA to set up a new external
+ KYC process. It will return the URL that the client must GET to begin the
+ KYC process. The SPA should probably open this URL in a new window or tab.
+ The ``$ID`` will be provided as part of the ``/kyc-info`` body. In
+ practice, ``$ID`` will encode both the ``$ACCESS_TOKEN`` and the index of
+ the selected measure (but this should be irrelevant for the client).
+
+ **Request:**
+
+ Use empty JSON body for now.
- // Keys are the names of the check(s).
- // The values are for now always empty objects.
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The KYC process was successfully initiated. The URL is in a
+ `KycProcessStartInformation` object.
+ :http:statuscode:`404 Not Found`:
+ The ``$ID`` is unknown to the exchange.
+ **Details:**
+
+ .. ts:def:: KycProcessStartInformation
+
+ interface KycProcessStartInformation {
+
+ // URL to open.
+ redirect_url: string;
}
-.. http:get:: /kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO
+ .. note::
+
+ As this endpoint is involved in every KYC check at the beginning, this
+ is also the place where we could integrate the payment process for the KYC fee
+ in the future (since **vATTEST**).
+
+.. http:get:: /kyc-proof/$PROVIDER_NAME?state=$H_PAYTO
+
+ Upon completion of the process at the external KYC provider, the provider
+ must redirect the client (browser) to trigger a GET request to a new
+ ``/kyc-proof/$H_PAYTO/$PROVIDER_NAME`` endpoint. Once this endpoint is
+ triggered, the exchange will pass the received arguments to the respective
+ logic plugin. The logic plugin will then (asynchronously) update the KYC
+ status of the user. The logic plugin should redirect the user to the KYC
+ SPA. This endpoint deliberately does not use the ``$ACCESS_TOKEN`` as the
+ external KYC provider should not learn that token.
- Endpoint accessed from the user's browser at the *end* of a
+ This endpoint is thus accessed from the user's browser at the *end* of a
KYC process, possibly providing the exchange with additional
credentials to obtain the results of the KYC process.
Specifically, the URL arguments should provide
information to the exchange that allows it to verify that the
user has completed the KYC process. The details depend on
- the logic, which is selected by the "$PROVIDER_SECTION".
+ the logic, which is selected by the "$PROVIDER_NAME".
While this is a GET (and thus safe, and idempotent), the operation
may actually trigger significant changes in the exchange's state.
@@ -5071,8 +5487,10 @@ regulatory compliance.
If the KYC plugin logic is OAuth 2.0, the query parameters are:
- :query code=CODE: OAuth 2.0 code argument.
- :query state=STATE: OAuth 2.0 state argument with the H_PAYTO.
+ :query code=CODE:
+ OAuth 2.0 code argument.
+ :query state=STATE:
+ OAuth 2.0 state argument with the H_PAYTO.
.. note::
@@ -5102,8 +5520,8 @@ regulatory compliance.
service within a reasonable time period.
-.. http:get:: /kyc-webhook/$PROVIDER_SECTION/*
-.. http:post:: /kyc-webhook/$PROVIDER_SECTION/*
+.. http:get:: /kyc-webhook/$PROVIDER_NAME/*
+.. http:post:: /kyc-webhook/$PROVIDER_NAME/*
.. http:get:: /kyc-webhook/$LOGIC/*
.. http:post:: /kyc-webhook/$LOGIC/*
@@ -5111,7 +5529,7 @@ regulatory compliance.
payment target. They provide information to the KYC logic of the exchange
that allows it to verify that the user has completed the KYC process. May
be a GET or a POST request, depending on the specific "$LOGIC" and/or the
- "$PROVIDER_SECTION".
+ "$PROVIDER_NAME".
**Request:**
@@ -5176,7 +5594,7 @@ designated account.
:http:statuscode:`451 Unavailable For Legal Reasons`:
This account has not yet passed the KYC checks.
The client must pass KYC checks before the reserve can be opened.
- The response will be an `KycNeededRedirect` object.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
@@ -5392,7 +5810,7 @@ designated account.
This account has not yet passed the KYC checks, hence wiring
funds to a non-origin account is not allowed.
The client must pass KYC checks before the reserve can be opened.
- The response will be an `KycNeededRedirect` object.
+ The response will be an `LegitimizationNeededResponse` object.
**Details:**
diff --git a/core/api-mailbox.rst b/core/api-mailbox.rst
index 34d27ded..bdb52c89 100644
--- a/core/api-mailbox.rst
+++ b/core/api-mailbox.rst
@@ -187,19 +187,19 @@ Receiving messages
interface MessageDeletionRequest {
- // Number of messages to delete. (Starting from the beginning
- // of the latest GET response).
- count: Integer;
+ // Number of messages to delete. (Starting from the beginning
+ // of the latest GET response).
+ count: Integer;
- // SHA-512 hash over all messages to delete.
- checksum: HashCode;
+ // SHA-512 hash over all messages to delete.
+ checksum: HashCode;
- // Signature by the mailbox's private key affirming
- // the deletion of the messages, of purpuse
- // ``TALER_SIGNATURE_WALLET_MAILBOX_DELETE_MESSAGES``.
- wallet_sig: EddsaSignature;
+ // Signature by the mailbox's private key affirming
+ // the deletion of the messages, of purpuse
+ // ``TALER_SIGNATURE_WALLET_MAILBOX_DELETE_MESSAGES``.
+ wallet_sig: EddsaSignature;
- }
+ }
**Response**
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index e06b262b..0ae7aa1f 100644
--- a/core/api-merchant.rst
+++ b/core/api-merchant.rst
@@ -123,7 +123,7 @@ such as the implemented version of the protocol and the currency used.
.. http:get:: /config
Return the protocol version and currency supported by this merchant backend.
- This specification corresponds to ``current`` protocol being version **v14**.
+ This specification corresponds to ``current`` protocol being version **v16**.
**Response:**
@@ -422,12 +422,13 @@ Making the payment
choice_index?: Integer;
// Output commitment. Hash over output token envelopes.
+ // @since protocol **vSUBSCRIBE**
h_outputs?: HashCode;
}
.. ts:def:: CoinPaySig
- export interface CoinPaySig {
+ interface CoinPaySig {
// Signature by the coin.
coin_sig: EddsaSignature;
@@ -591,7 +592,7 @@ Querying payment status
.. ts:def:: StatusPaidResponse
- interface StatusPaid {
+ interface StatusPaidResponse {
// Was the payment refunded (even partially, via refund or abort)?
refunded: boolean;
@@ -1701,9 +1702,165 @@ Taler merchant backend to process payments *without* using its inventory
management.
+Managing product categories
+---------------------------
+
+.. http:get:: [/instances/$INSTANCE]/private/categories
+
+ This request returns the known product categories
+ and the number of products in each category.
+ Since API version **v16**.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The body is a `CategoryListResponse`.
+
+ **Details:**
+
+ .. ts:def:: CategoryListResponse
+
+ interface CategoryListResponse {
+
+ // Array with all of the categories we know.
+ categories: CategoryListEntry[];
+
+ }
+
+ .. ts:def:: CategoryListEntry
+
+ interface CategoryListEntry {
+
+ // Unique number for the category.
+ category_id: Integer;
+
+ // Name of the category.
+ name: string;
+
+ // Translations of the name into various
+ // languages.
+ name_i18n?: { [lang_tag: string]: string };
+
+ // Number of products in this category.
+ // A product can be in more than one category.
+ product_count: Integer;
+
+ }
+
+.. http:get:: [/instances/$INSTANCE]/private/categories/$CATEGORY_ID
+
+ This request returns the known products in the given
+ category.
+ Since API version **v16**.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The body is a `CategoryProductList`.
+
+ **Details:**
+
+ .. ts:def:: CategoryProductList
+
+ interface CategoryProductList {
+
+ // Name of the category.
+ name: string;
+
+ // Translations of the name into various
+ // languages.
+ name_i18n?: { [lang_tag: string]: string };
+
+ // The products in this category.
+ products: ProductSummary[];
+
+ }
+
+ .. ts:def:: ProductSummary
+
+ interface ProductSummary {
+
+ // Product ID to use.
+ product_id: string;
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n?: { [lang_tag: string]: string };
+
+ }
+
+
+.. http:post:: [/instances/$INSTANCE]/private/categories
+
+ This is used to create a new category for the inventory.
+ Since API version **v16**.
+
+ **Request:**
+
+ The request is a `CategoryCreateRequest`.
+
+ **Response:**
+
+ :http:statuscode:`200 Ok`:
+ The response is a `CategoryCreatedResponse`.
+
+ **Details:**
+
+ .. ts:def:: CategoryCreateRequest
+
+ interface CategoryCreateRequest {
+
+ // Name of the category.
+ name: string;
+
+ // Translations of the name into various
+ // languages.
+ name_i18n?: { [lang_tag: string]: string };
+
+ }
+
+ .. ts:def:: CategoryCreatedResponse
+
+ interface CategoryCreatedResponse {
+
+ // Number of the newly created category.
+ category_id: Integer;
+ }
+
+
+.. http:patch:: [/instances/$INSTANCE]/private/categories/$CATEGORY_ID
+
+ This is used to edit a category.
+ Since API version **v16**.
+
+ **Request:**
+
+ The request is a `CategoryCreateRequest`.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The category was modified successfully.
+
+.. http:delete:: [/instances/$INSTANCE]/private/categories/$CATEGORY_ID
+
+ This is used to delete a category.
+ Since API version **v16**.
+
+ **Response:**
+
+ :http:statuscode:`204 No Content`:
+ The category was deleted.
+ :http:statuscode:`404 Not Found`:
+ The category was possibly already deleted or is unknown.
+
+
Adding products to the inventory
--------------------------------
+
.. http:post:: [/instances/$INSTANCE]/private/products
This is used to add a product to the inventory.
@@ -1719,6 +1876,7 @@ Adding products to the inventory
:http:statuscode:`409 Conflict`:
The backend already knows a product with this product ID, but with different details.
+ **Details:**
.. ts:def:: ProductAddDetail
@@ -1733,6 +1891,11 @@ Adding products to the inventory
// Map from IETF BCP 47 language tags to localized descriptions.
description_i18n?: { [lang_tag: string]: string };
+ // Categories into which the product belongs.
+ // Used in the POS-endpoint.
+ // Since API version **v16**.
+ categories?: Integer[];
+
// Unit in which the product is measured (liters, kilograms, packages, etc.).
unit: string;
@@ -1808,6 +1971,11 @@ Adding products to the inventory
// Unit in which the product is measured (liters, kilograms, packages, etc.).
unit: string;
+ // Categories into which the product belongs.
+ // Used in the POS-endpoint.
+ // Since API version **v16**.
+ categories?: Integer[];
+
// The price for one ``unit`` of the product. Zero is used
// to imply that this product is not sold separately, or
// that the price is not fixed, and must be supplied by the
@@ -1878,6 +2046,9 @@ Inspecting inventory
// ``product_serial_id`` of the product in the database.
product_serial: Integer;
+
+ // TODO: also return description/description_i18n here?
+ // TODO: what's the point in returning product_serial?
}
@@ -1906,6 +2077,10 @@ Inspecting inventory
// Unit in which the product is measured (liters, kilograms, packages, etc.).
unit: string;
+ // Categories into which the product belongs.
+ // Since API version **v16**.
+ categories: Integer[];
+
// The price for one ``unit`` of the product. Zero is used
// to imply that this product is not sold separately, or
// that the price is not fixed, and must be supplied by the
@@ -1917,7 +2092,8 @@ Inspecting inventory
image: ImageDataUrl;
// A list of taxes paid by the merchant for one unit of this product.
- taxes: Tax[];
+ // Optional since **v15**.
+ taxes?: Tax[];
// Number of units of the product in stock in sum in total,
// including all existing sales ever. Given in product-specific
@@ -1932,7 +2108,8 @@ Inspecting inventory
total_lost: Integer;
// Identifies where the product is in stock.
- address: Location;
+ // Optional since **v15**.
+ address?: Location;
// Identifies when we expect the next restocking to happen.
next_restock?: Timestamp;
@@ -1942,6 +2119,93 @@ Inspecting inventory
}
+.. http:get:: [/instances/$INSTANCE]/private/pos
+
+ This is used to return the point-of-sale (POS) configuration with full details on all items in the inventory.
+
+ Endpoint was introduced in protocol **v15**.
+
+ **Response:**
+
+ :http:statuscode:`200 OK`:
+ The backend has successfully returned the inventory. Returns
+ a `FullInventoryDetailsResponse`.
+ :http:statuscode:`404 Not found`:
+ The backend has does not know about the instance.
+
+ .. ts:def:: FullInventoryDetailsResponse
+
+ interface FullInventoryDetailsResponse {
+
+ // List of products that are present in the inventory.
+ products: MerchantPosProductDetail[];
+
+ // List of categories in the inventory.
+ categories: MerchantCategory[];
+
+ }
+
+ .. ts:def:: MerchantPosProductDetail
+
+ interface MerchantPosProductDetail {
+
+ // A unique numeric ID of the product
+ product_serial: number;
+
+ // A merchant-internal unique identifier for the product
+ product_id?: string;
+
+ // A list of category IDs this product belongs to.
+ // Typically, a product only belongs to one category, but more than one is supported.
+ categories: number[];
+
+ // Human-readable product description.
+ description: string;
+
+ // Map from IETF BCP 47 language tags to localized descriptions.
+ description_i18n: { [lang_tag: string]: string };
+
+ // Unit in which the product is measured (liters, kilograms, packages, etc.).
+ unit: string;
+
+ // The price for one ``unit`` of the product. Zero is used
+ // to imply that this product is not sold separately, or
+ // that the price is not fixed, and must be supplied by the
+ // front-end. If non-zero, this price MUST include applicable
+ // taxes.
+ price: Amount;
+
+ // An optional base64-encoded product image.
+ image?: ImageDataUrl;
+
+ // A list of taxes paid by the merchant for one unit of this product.
+ taxes?: Tax[];
+
+ // Number of units of the product in stock in sum in total,
+ // including all existing sales ever. Given in product-specific
+ // units.
+ // Optional, if missing treat as "infinite".
+ total_stock?: Integer;
+
+ // Minimum age buyer must have (in years).
+ minimum_age?: Integer;
+
+ }
+
+ .. ts:def:: MerchantCategory
+
+ interface MerchantCategory {
+ // A unique numeric ID of the category
+ id: number;
+
+ // The name of the category. This will be shown to users and used in the order summary.
+ name: string;
+
+ // Map from IETF BCP 47 language tags to localized names
+ name_i18n?: { [lang_tag: string]: string };
+ }
+
+
Reserving inventory
-------------------
diff --git a/core/api-terminal.rst b/core/api-terminal.rst
index 33be98a9..91d9b7e6 100644
--- a/core/api-terminal.rst
+++ b/core/api-terminal.rst
@@ -74,6 +74,11 @@ Config
// Terminal provider display name to be used in user interfaces.
provider_name: string;
+ // The currency supported by this Terminal-API
+ // must be the same as the currency specified
+ // in the currency field of the wire gateway config
+ currency: string;
+
// Wire transfer type supported by the terminal.
// FIXME: needed?
wire_type: string;
diff --git a/design-documents/023-taler-kyc.rst b/design-documents/023-taler-kyc.rst
index c20844b3..786b0ca7 100644
--- a/design-documents/023-taler-kyc.rst
+++ b/design-documents/023-taler-kyc.rst
@@ -200,12 +200,12 @@ user for *voluntary* KYC processes related to attestation (#7365).
Proposed Solution
=================
-The main state of an account is represented by a set of `KycRules` (the
+The main state of an account is represented by a set of `KYC rules <KycRule>` (the
`LegitimizationRuleSet`) which specify the current *rules* to apply to
transactions involving the account. Rules can *exposed* to the account owner,
or can be secret. Each *rule* specifies certain *conditions* which, if met,
-*trigger* a single specific *measure*. After a *rule* was *triggered* and
-before the *outcome* of the respective *measure* has been produced (say
+*trigger* a set of *measures*. After a *rule* was *triggered* and
+before the *outcome* of a respective *measure* has been produced (say
because the user did not yet enter their data or the AML officer is still
reviewing the case), the existing rules remain in force. Rules have a display
priority, and if a second rule with a higher display priority is also
@@ -431,48 +431,9 @@ expose merchant public key to SPA for wire transfer if needed for KYC.
^^^^^^^^^^^^
When KYC operations are required, various endpoints may respond with a
-``451 Unavailable for Legal Reasons`` status code and a `KycNeededRedirect`
+``451 Unavailable for Legal Reasons`` status code and a `LegitimziationNeededResponse`
body.
- .. ts:def:: KycNeededRedirect
-
- interface KycNeededRedirect {
-
- // Numeric `error code <error-codes>` unique to the condition.
- // Should always be ``TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED``.
- code: number;
-
- // Human-readable description of the error, i.e. "missing parameter",
- // "commitment violation", ... Should give a human-readable hint
- // about the error's nature. Optional, may change without notice!
- hint?: string;
-
- // Hash of the payto:// account URI for which KYC
- // is required.
- h_payto: PaytoHash;
-
- // Public key associated with the account. The client must sign
- // the initial request for the KYC status using the corresponding
- // private key. Will be either a reserve public key or a merchant
- // (instance) public key.
- //
- // Absent if no public key is currently associated
- // with the account and the client MUST thus first
- // credit the exchange via an inbound wire transfer
- // to associate a public key with the debited account.
- account_pub?: EddsaPublicKey;
-
- // Identifies a set of measures that were triggered and that are
- // now preventing this operation from proceeding. Gives the
- // account holder a starting point for understanding why the
- // transaction was blocked and how to lift it. The account holder
- // should use the number to check for the account's AML/KYC status
- // using the ``/kyc-check/$REQUIREMENT_ROW`` endpoint.
- requirement_row: Integer;
-
- }
-
-
New endpoints
^^^^^^^^^^^^^
@@ -489,7 +450,7 @@ New endpoints
The requirement row of the ``/kyc-check/`` endpoint encodes the
legitimization measure's serial number. It is returned in
- `KycNeededRedirect` responses via the ``requirement_row`` field.
+ `LegitimizationNeededResponse` responses via the ``requirement_row`` field.
Given a valid pair of requirement row and account owner signature, the
``/kyc-check/`` endpoint returns either just the KYC status or redirects the
@@ -543,79 +504,6 @@ New endpoints
:http:statuscode:`404 Not found`:
The requirement row is unknown.
- **Details:**
-
- .. ts:def:: AccountKycStatus
-
- interface AccountKycStatus {
-
- // Current AML state for the target account. True if
- // operations are not happening due to staff processing
- // paperwork *or* due to legal requirements (so the
- // client cannot do anything but wait).
- //
- // Note that not every AML staff action may be legally
- // exposed to the client, so this is merely a hint that
- // a client should be told that AML staff is currently
- // reviewing the account. AML staff *may* review
- // accounts without this flag being set!
- aml_review: boolean;
-
- // Access token needed to construct the ``/kyc-spa/``
- // URL that the user should open in a browser to
- // proceed with the KYC process (optional if the status
- // type is ``200 Ok``, mandatory if the HTTP status
- // is ``202 Accepted``).
- access_token: AccountAccessToken;
-
- // Array with limitations that currently apply to this
- // account and that may be increased or lifted if the
- // KYC check is passed.
- // Note that additional limits *may* exist and not be
- // communicated to the client. If such limits are
- // reached, this *may* be indicated by the account
- // going into ``aml_review`` state. However, it is
- // also possible that the exchange may legally have
- // to deny operations without being allowed to provide
- // any justification.
- // The limits should be used by the client to
- // possibly structure their operations (e.g. withdraw
- // what is possible below the limit, ask the user to
- // pass KYC checks or withdraw the rest after the time
- // limit is passed, warn the user to not withdraw too
- // much or even prevent the user from generating a
- // request that would cause it to exceed hard limits).
- limits?: AccountLimit[];
-
- }
-
- .. ts:def:: AccountLimit
-
- interface AccountLimit {
-
- // Operation that is limited.
- // Must be one of "WITHDRAW", "DEPOSIT", "P2P-RECEIVE"
- // or "WALLET-BALANCE".
- operation_type: string;
-
- // Timeframe during which the limit applies.
- timeframe: RelativeTime;
-
- // Maximum amount allowed during the given timeframe.
- // Zero if the operation is simply forbidden.
- threshold: Amount;
-
- // True if this is a soft limit that could be raised
- // by passing KYC checks. Clients *may* deliberately
- // try to cross limits and trigger measures resulting
- // in 451 responses to begin KYC processes.
- // Clients that are aware of hard limits *should*
- // inform users about the hard limit and prevent flows
- // in the UI that would cause violations of hard limits.
- soft_limit: boolean;
- }
-
-
.. http:get:: /kyc-spa/$ACCESS_TOKEN
.. http:get:: /kyc-spa/$FILENAME
@@ -656,72 +544,13 @@ New endpoints
**Response:**
+ *Etag*: Will be set to the serial ID of the measure. Used for long-polling (only for 200 OK responses).
+
:http:statuscode:`200 OK`:
The body is a `KycProcessClientInformation`.
-
- *Etag*: Will be set to the serial ID of the measure. Used for long-polling.
-
- .. ts:def:: KycProcessClientInformation
-
- interface KycProcessClientInformation {
-
- // List of requirements.
- requirements?: { name : KycRequirementInformation};
-
- // True if the client is expected to eventually satisfy all requirements.
- // Default (if missing) is false.
- is_and_combinator?: boolean
-
- // List of available voluntary checks the client could pay for.
- // Since **vATTEST**.
- voluntary_checks?: { name : KycCheckInformation};
- }
-
- .. ts:def:: KycRequirementInformation
-
- interface KycRequirementInformation {
-
- // Which form should be used? Common values include "INFO"
- // (to just show the descriptions but allow no action),
- // "LINK" (to enable the user to obtain a link via
- // ``/kyc-start/``) or any build-in form name supported
- // by the SPA.
- form: string;
-
- // English description of the requirement.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized
- // description texts.
- description_i18n ?: { [lang_tag: string]: string };
-
- // ID of the requirement, useful to construct the
- // ``/kyc-upload/$ID`` or ``/kyc-start/$ID`` endpoint URLs.
- // Present if and only if "form" is not "INFO". The
- // ``$ID`` value may itself contain ``/`` or ``?`` and
- // basically encode any URL path (and optional arguments).
- id?: string;
-
- }
-
- .. ts:def:: KycCheckInformation
-
- // Since **vATTEST**.
- interface KycCheckInformation {
-
- // English description of the check.
- description: string;
-
- // Map from IETF BCP 47 language tags to localized
- // description texts.
- description_i18n ?: { [lang_tag: string]: string };
-
- }
-
:http:statuscode:`204 No Content`:
There are no open KYC requirements or possible voluntary checks
the client might perform.
-
:http:statuscode:`304 Not Modified`:
The KYC requirements did not change.
@@ -749,7 +578,7 @@ New endpoints
The ``$ID`` is unknown to the exchange.
:http:statuscode:`409 Conflict`:
The upload conflicts with a previous upload.
- :http:statuscode:`413 Content Too Large`:
+ :http:statuscode:`413 Request Entity Too Large`:
The body is too large.
.. http:post:: /kyc-start/$ID
@@ -771,16 +600,6 @@ New endpoints
The KYC process was successfully initiated. The URL is in a
`KycProcessStartInformation` object.
- **Details:**
-
- .. ts:def:: KycProcessStartInformation
-
- interface KycProcessStartInformation {
-
- // URL to open.
- redirect_url: string;
- }
-
:http:statuscode:`404 Not Found`:
The ``$ID`` is unknown to the exchange.
@@ -913,29 +732,7 @@ New endpoints
This response comes with a standard `ErrorDetail` response.
:http:statuscode:`451 Unavailable for Legal Reasons`:
The wallet must undergo a KYC check. A KYC ID was created.
- The response will be a `KycNeededRedirect` object.
-
- **Details:**
-
- .. ts:def:: WalletKycRequest
-
- interface WalletKycRequest {
-
- // Balance threshold (not necessarily exact balance)
- // to be crossed by the wallet that (may) trigger
- // additional KYC requirements.
- balance: Amount;
-
- // EdDSA signature of the wallet affirming the
- // request, must be of purpose
- // ``TALER_SIGNATURE_WALLET_ACCOUNT_SETUP``
- reserve_sig: EddsaSignature;
-
- // long-term wallet reserve-account
- // public key used to create the signature.
- reserve_pub: EddsaPublicKey;
- }
-
+ The response will be a `LegitimizationNeededResponse` object.
.. http:get:: /aml/$OFFICER_PUB/measures
@@ -959,86 +756,6 @@ New endpoints
Information about possible measures is returned in a
`AvailableMeasureSummary` object.
- **Details:**
-
- .. ts:def:: AvailableMeasureSummary
-
- interface AvailableMeasureSummary {
-
- // Available original measures that can be
- // triggered directly by default rules.
- roots: { "$measure_name" : MeasureInformation; };
-
- // Available AML programs.
- programs: { "$prog_name" : AmlProgramRequirement; };
-
- // Available KYC checks.
- checks: { "$check_name" : KycCheckInformation; };
-
- }
-
- .. ts:def:: MeasureInformation
-
- interface MeasureInformation {
-
- // Name of a KYC check.
- check_name: string;
-
- // Name of an AML program.
- prog_name: string;
-
- // Context for the check. Optional.
- context?: Object;
-
- }
-
- .. ts:def:: AmlProgramRequirement
-
- interface AmlProgramRequirement {
-
- // Description of what the AML program does.
- description: string;
-
- // List of required field names in the context to run this
- // AML program. SPA must check that the AML staff is providing
- // adequate CONTEXT when defining a measure using this program.
- context: string[];
-
- // List of required attribute names in the
- // input of this AML program. These attributes
- // are the minimum that the check must produce
- // (it may produce more).
- inputs: string[];
-
- }
-
- .. ts:def:: KycCheckInformation
-
- interface KycCheckInformation {
-
- // Description of the KYC check. Should be shown
- // to the AML staff but will also be shown to the
- // client when they initiate the check in the KYC SPA.
- description: string;
- description_i18n: {};
-
- // Names of the fields that the CONTEXT must provide
- // as inputs to this check.
- // SPA must check that the AML staff is providing
- // adequate CONTEXT when defining a measure using
- // this check.
- requires: string[];
-
- // Names of the attributes the check will output.
- // SPA must check that the outputs match the
- // required inputs when combining a KYC check
- // with an AML program into a measure.
- outputs: string[];
-
- // Name of a root measure taken when this check fails.
- fallback: string;
- }
-
.. http:get:: /aml/$OFFICER_PUB/kyc-statistics/$NAME
Returns the number of KYC events matching the given event type ``$NAME`` in
@@ -1064,13 +781,8 @@ New endpoints
**Response:**
- .. ts:def:: EventCounter
-
- interface EventCounter {
- // Number of events of the specified type in
- // the given range.
- counter: Integer;
- }
+ :http:statuscode:`200 OK`:
+ The responds will be an `EventCounter` message.
.. http:get:: /aml/$OFFICER_PUB/decisions
@@ -1116,31 +828,6 @@ New endpoints
:http:statuscode:`409 Conflict`:
The designated AML account is not enabled.
- **Details:**
-
- .. ts:def:: AmlDecisions
-
- interface AmlDecisions {
-
- // Array of AML decisions matching the query.
- records: AmlDecisions[];
- }
-
- .. ts:def:: AmlRecord
-
- interface AmlRecord {
-
- // Which payto-address is this record about.
- // Identifies a GNU Taler wallet or an affected bank account.
- h_payto: PaytoHash;
-
- // Row ID of the record. Used to filter by offset.
- rowid: Integer;
-
- // FIXME: more fields here!
- }
-
-
.. http:get:: /aml/$OFFICER_PUB/attributes/$H_PAYTO
Obtain attributes obtained as part of AML/KYC processes for a
@@ -1176,40 +863,7 @@ New endpoints
:http:statuscode:`409 Conflict`:
The designated AML account is not enabled.
- .. ts:def:: KycAttributes
-
- interface KycAttributes {
-
- // Matching KYC attribute history of the account.
- details: KycDetail[];
-
- }
-
- .. ts:def:: KycDetail
-
- // FIXME: bad name?
- interface KycDetail {
-
- // Row ID of the record. Used to filter by offset.
- rowid: Integer;
-
- // Name of the configuration section that specifies the provider
- // which was used to collect the attributes. NULL if they were
- // just uploaded via a form by the account owner.
- provider_section?: string;
-
- // The collected KYC data. NULL if the attribute data could not
- // be decrypted (internal error of the exchange, likely the
- // attribute key was changed).
- attributes?: Object;
-
- // Time when the KYC data was collected
- collection_time: Timestamp;
-
- }
-
-
- .. http:post:: /aml/$OFFICER_PUB/decision
+.. http:post:: /aml/$OFFICER_PUB/decision
Make an AML decision. Triggers the respective action and
records the justification.
@@ -1231,35 +885,6 @@ New endpoints
The designated AML account is not enabled or a more recent
decision was already submitted.
- **Details:**
-
- .. ts:def:: AmlDecision
-
- interface AmlDecision {
-
- // Human-readable justification for the decision.
- justification: string;
-
- // Which payto-address is the decision about?
- // Identifies a GNU Taler wallet or an affected bank account.
- h_payto: PaytoHash;
-
- // What are the new rules?
- new_rules: LegitimizationRuleSet;
-
- // True if the account should remain under investigation by AML staff.
- bool keep_investigating;
-
- // When was the decision made?
- decision_time: Timestamp;
-
- // Signature by the AML officer over a `TALER_AmlDecisionPS`.
- // Must have purpose ``TALER_SIGNATURE_MASTER_AML_KEY``.
- officer_sig: EddsaSignature;
-
- }
-
-
Modifications to existing endpoints
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1311,17 +936,6 @@ description of the high-level process for different providers.
# Which plugin is responsible for this provider?
LOGIC = PLUGIN_NAME
- # Optional cost, useful if clients want to voluntarily
- # trigger authentication procedures for attestation.
- # Since **vATTEST**.
- COST = EUR:5
-
- # Plus additional logic-specific options, e.g.:
- AUTHORIZATION_TOKEN = superdupersecret
-
- # Other logic-specific internal options (example):
- FORM_ID = business_legi_form
-
# Name of a program to run on the output of the plugin
# to convert the result into the desired set of attributes.
# The converter must create a log for the system administrator
@@ -1334,6 +948,17 @@ description of the high-level process for different providers.
# (when run regularly).
CONVERTER = taler-exchange-helper-$NAME
+ # Optional cost, useful if clients want to voluntarily
+ # trigger authentication procedures for attestation.
+ # Since **vATTEST**.
+ COST = EUR:5
+
+ # Plus additional logic-specific options, e.g.:
+ AUTHORIZATION_TOKEN = superdupersecret
+
+ # Other logic-specific internal options (example):
+ FORM_ID = business_legi_form
+
Configuration of possible KYC/AML checks
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1364,6 +989,7 @@ providers, one per configuration section:
VOLUNTARY = YES/NO
# Provider id, present only if type is LINK.
+ # Refers to a ``kyc-provider-$PROVIDER_ID`` section.
PROVIDER_ID = id
# Name of the SPA form, if type is FORM
@@ -1381,7 +1007,7 @@ providers, one per configuration section:
DESCRIPTION_I18N = "{"en":"Upload scan of your passport"}"
# ';'-separated list of fields that the CONTEXT must
- # provided as inputs to this check. For example,
+ # provide as inputs to this check. For example,
# for a FORM of type CHOICE, this might state
# ``choices: string[];``. The type after the ":"
# is for now purely for documentation and is
@@ -1391,8 +1017,11 @@ providers, one per configuration section:
# Description of the outputs provided by the check.
# Basically, the check's output is expected to
- # provide the following fields as inputs into
+ # provide the following fields as attribute inputs into
# a subsequent AML program.
+ # Only given for type FORM; INFO never has any outputs,
+ # and for type LINK we can obtain the same information
+ # from the CONVERTER via ``--list-outputs``.
OUTPUTS = business_name street city country registration
# **original** measure to take if the check fails
@@ -1422,7 +1051,7 @@ configuration section:
[kyc-rule-$RULE_NAME]
- # Operation that triggers this legitimization.
+ # Operation that triggers this rule.
# Must be one of WITHDRAW, DEPOSIT, P2P-RECEIVE
# or WALLET-BALANCE.
OPERATION_TYPE = WITHDRAW
@@ -1436,8 +1065,8 @@ configuration section:
# (under this set of rules).
NEXT_MEASURES = SWISSNESS KYB
- # "yes" if all NEXT_MEASURES will eventually need
- # to be satisfied, "no" if the user has a choice between
+ # "YES" if all NEXT_MEASURES will eventually need
+ # to be satisfied, "NO" if the user has a choice between
# them. Not actually enforced by the exchange, but
# primarily used to inform the user whether this is
# an "and" or "or". YES for "and".
@@ -1450,7 +1079,7 @@ configuration section:
# Defaults to NO if not set.
EXPOSED = YES
- # Threshold amount above which the legitimization is
+ # Threshold amount above which the rule is
# triggered. The total must be exceeded in the given
# timeframe.
THRESHOLD = KUDOS:100
@@ -1460,7 +1089,7 @@ configuration section:
# Ignored for WALLET-BALANCE. Can be 'forever'.
TIMEFRAME = 30 days
- # Enabled (default is NO)
+ # Set to YES to enable the rule (default is NO)
ENABLED = NO
@@ -1487,113 +1116,6 @@ AML programs are helper programs that can:
This is the default behavior if no command-line switches
are provided.
-.. ts:def:: AmlProgramInput
-
- interface AmlProgramInput {
-
- // JSON object that was provided as
- // part of the *measure*. This JSON object is
- // provided under "context" in the main JSON object
- // input to the AML program. This "context" should
- // satify both the REQUIRES clause of the respective
- // check and the output of "--requires" from the
- // AML program's command-line option.
- context?: Object;
-
- // JSON object that captures the
- // output of a ``[kyc-provider-]`` or (HTML) FORM.
- // The keys in the JSON object will be the attribute
- // names and the values must be strings representing
- // the data. In the case of file uploads, the data
- // MUST be base64-encoded.
- attributes: Object;
-
- // JSON array with the results of historic
- // AML desisions about the account.
- aml_history: AmlDecisionDetail[];
-
- // JSON array with the results of historic
- // KYC data about the account.
- kyc_history: KycDetail[];
-
- }
-
-.. ts:def:: AmlOutcome
-
- interface AmlOutcome {
-
- // Should the client's account be investigated
- // by AML staff?
- // Defaults to false.
- to_investigate?: boolean;
-
- // Free-form properties about the account.
- // Can be used to store properties such as PEP,
- // risk category, type of business, hits on
- // sanctions lists, etc.
- properties?: AccountProperties;
-
- // Types of events to add to the KYC events table.
- // (for statistics).
- events?: string[];
-
- // KYC rules to apply. Note that this
- // overrides *all* of the default rules
- // until the ``expiration_time`` and specifies
- // the successor measure to apply after the
- // expiration time.
- new_rules: LegitimizationRuleSet;
-
- }
-
-.. ts:def:: KycRule
-
- interface KycRule {
-
- // Type of operation to which the rule applies.
- operation_type: string;
-
- // The measures will be taken if the given
- // threshold is crossed over the given timeframe.
- threshold: Amount;
-
- // Over which duration should the ``threshold`` be
- // computed. All amounts of the respective
- // ``operation_type`` will be added up for this
- // duration and the sum compared to the ``threshold``.
- timeframe: RelativeTime;
-
- // Array of names of measures to apply.
- // Names listed can be original measures or
- // custom measures from the `AmlOutcome`.
- // A special measure "verboten" is used if the
- // threshold may never be crossed.
- measures: string[];
-
- // True if the rule (specifically, operation_type,
- // threshold, timeframe) and the general nature of
- // the measures (verboten or approval required)
- // should be exposed to the client.
- // Defaults to "false" if not set.
- exposed?: boolean;
-
- // True if all the measures will eventually need to
- // be satisfied, false if any of the measures should
- // do. Primarily used by the SPA to indicate how
- // the measures apply when showing them to the user;
- // in the end, AML programs will decide after each
- // measure what to do next.
- // Default (if missing) is false.
- is_and_combinator?: boolean;
-
- // If multiple rules apply to the same account
- // at the same time, the number with the highest
- // rule determines which set of measures will
- // be activated and thus become visible for the
- // user.
- display_priority: integer;
- }
-
If the AML program fails (exits with a failure code or
does not provide well-formed JSON output) the AML/KYC
process continues with the FALLBACK measure. This should
@@ -1646,8 +1168,9 @@ Finally, the configuration specifies a set of
# just an empty JSON object if there is none.
CONTEXT = {"choices":["individual","business"]}
- # Program to run on the context and check data to
+ # Program name to run on the context and check data to
# determine the outcome and next measure.
+ # Refers to a ``[aml-program-$PROG_NAME]`` section name.
PROGRAM = taler-aml-program
If no ``CHECK_NAME`` is provided at all, the AML ``PROGRAM`` is to be run
@@ -1738,6 +1261,8 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
)
PARTITION BY HASH (access_token);
+ COMMENT ON TABLE legitimization_measures
+ IS 'Rules that have been triggered for the account (FIXME: check this is consistent with usage)';
COMMENT ON COLUMN legitimization_measures.access_token
IS 'Used to uniquely identify the account and as a symmetric access control mechanism for the SPA';
COMMENT ON COLUMN legitimization_measures.start_time
@@ -1885,86 +1410,13 @@ on GET ``/deposits/`` with the respective legitimization requirement row.
The ``jmeasures`` JSON in the ``legitimization_measures``
-table has is of type `LegitimizationMeasures`:
-
-.. ts:def:: LegitimizationMeasures
-
- interface LegitimizationMeasures {
-
- // Array of legitimization measures that
- // are to be applied.
- measures: MeasureInformation[];
-
- // True if the client is expected to eventually satisfy all requirements.
- // Default (if missing) is false.
- is_and_combinator?: boolean;
- }
-
+table is of type `LegitimizationMeasures`.
The ``jnew_rules`` JSON in the ``legitimization_outcomes``
-table has is of type `LegitimizationRuleSet`:
-
-.. ts:def:: LegitimizationRuleSet
-
- interface LegitimizationRuleSet {
-
- // When does this set of rules expire and
- // we automatically transition to the successor
- // measure?
- expiration_time: Timestamp;
-
- // Name of the measure to apply when the expiration time is
- // reached. If not set, we refer to the default
- // set of rules (and the default account state).
- successor_measure?: string;
-
- // Legitimization rules that are to be applied
- // to this account.
- rules: KycRule[];
-
- // Custom measures that KYC rules and the
- // ``successor_measure`` may refer to.
- custom_measures: { "$measure_name" : MeasureInformation; };
- }
-
-
-The ``jproperties`` JSON in the ``legitimization_outcomes`` table has is of
-type `AccountProperties`. All fields in this object are optional. The actual
-properties collected depend fully on the discretion of the exchange operator;
-however, some common fields are standardized and thus described here.
-
-.. ts:def:: AccountProperties
-
- interface AccountProperties {
-
- // True if this is a politically exposed account.
- // Rules for classifying accounts as politically
- // exposed are country-dependent.
- pep?: boolean;
-
- // True if this is a sanctioned account.
- // Rules for classifying accounts as sanctioned
- // are country-dependent.
- sanctioned?: boolean;
-
- // True if this is a high-risk account.
- // Rules for classifying accounts as at-risk
- // are exchange operator-dependent.
- high_risk?: boolean;
-
- // Business domain of the account owner.
- // The list of possible business domains is
- // operator- or country-dependent.
- business_domain?: string;
-
- // Is the client's account currently frozen?
- is_frozen?: boolean;
-
- // Was the client's account reported to the authorities?
- was_reported?: boolean;
-
- }
+table is of type `LegitimizationRuleSet`.
+The ``jproperties`` JSON in the ``legitimization_outcomes`` table is of
+type `AccountProperties`.
KYC forms
diff --git a/design-documents/037-wallet-transactions-lifecycle.rst b/design-documents/037-wallet-transactions-lifecycle.rst
index 535e4ff8..5579d1b7 100644
--- a/design-documents/037-wallet-transactions-lifecycle.rst
+++ b/design-documents/037-wallet-transactions-lifecycle.rst
@@ -172,9 +172,18 @@ the courts) to avoid losing the funds for good.
Transaction Type: Withdrawal
----------------------------
+* ``dialog(proposed)``
+
+ Initial dialog state for bank-integrated withdrawals. In this state, the user must confirm
+ the withdrawal to proceed, and possibly provide further information (such as the amount).
+
+ Depending on how a bank-integrated withdrawal transaction is created,
+ it starts either in this state or in ``pending(bank-register-reserve)``.
+
+
* ``pending(bank-register-reserve)``
- Initial state for bank-integrated withdrawals. The wallet submits the reserve public key
+ Initial active state for bank-integrated withdrawals. The wallet submits the reserve public key
and selected exchange to the bank (via the bank integration API). Note that if the
user aborts at this stage, we do not know if the bank is in the confirmation stage,
so we must still *try* to abort the transaction at the bank.
diff --git a/design-documents/046-mumimo-contracts.rst b/design-documents/046-mumimo-contracts.rst
index 8cb35316..7d2197d4 100644
--- a/design-documents/046-mumimo-contracts.rst
+++ b/design-documents/046-mumimo-contracts.rst
@@ -326,7 +326,7 @@ The contract terms v1 will have the following structure:
// Start of the validity period of the token.
valid_after: Timestamp;
- // Number of tokens to be yelded.
+ // Number of tokens to be issued.
// Defaults to one if the field is not provided.
number?: Integer;
diff --git a/design-documents/053-wallet-ui.rst b/design-documents/053-wallet-ui.rst
index 57f9b1f2..44d82def 100644
--- a/design-documents/053-wallet-ui.rst
+++ b/design-documents/053-wallet-ui.rst
@@ -128,15 +128,8 @@ Layout
Issues
^^^^^^
-* WebEx (minor): Inconsistent to aldready have
- the "add" and "send" buttons on this page.
* WebEx (image): Screenshot does not show any
pending transactions.
-* Android (text): Uses "Exchange:" which is
- not user-facing terminology. Should only show the URLs.
-* Android (minor): Screenshot shows only a "pending"
- badge, which seems redundant given "+10 KUDOS inbound"
- (too much information?)
* iOS (major): Way too much detail shown, way too
much explanation text, too many operations
(send money, request payment, spend test money!!!!);
@@ -150,7 +143,6 @@ Issues
'tab' entirely!
* iOS (text): bad icon for "pending outgoing", the
"minus" sign does not give me good associations here
-* Android (text): Title should be *Balances* (plural)
* Android (minor): No "add" and "Send $CURRENCY" buttons
on this page.
@@ -288,8 +280,6 @@ Issues
payments and debit and POS) and again same icon
for invoice and withdraw) is sub-optimal. Should
pick unique icons for each type of operation.
-* Android (text): Button labels should just be
- "Send" and "Receive" (without "funds").
* iOS (text): Iconography (+ / -) is also not
expressive and redundant with the colors.
* iOS (text): Sign of the operation (+ / -) should
@@ -587,9 +577,7 @@ Issues
the amount **3** times. Maybe only show the amount
on top with the wire transfer details, and then at
the bottom show the fee ONCE *if* there is one?
-* Android(text): uses "exchange", which is verboten
* iOS(text): receiver name is missing, MUST be before IBAN
-* Android(text): wire transfer subject is third, should be first
* WebEx(screenshot): the screenshot does not show how to select an alternative bank (see: Netzbon). Would be nice to show that.
* Android(screenshot): the screenshot does not show how to select an alternative bank (see: Netzbon). Would be nice to show that.
@@ -598,8 +586,6 @@ Issues
Adoption
^^^^^^^^
-* WebEx/Android(text): Probably best to take the full text of the 3 steps from iOS (but add receiver name in step 2!) to provide very precise instructions.
-* WebEx/Android(text): "Open in banking app" is likely unclear that this requires Payto://-support. Use the long text from iOS everywhere!
* iOS(minor): the way to switch between different banks is not as clear as it was on WebEx. Proposal: use notebook tabs similar to how it is done on WebEx (IIRC?)
* Android: transaction status is not shown ("pending" on all other platforms. This is actually *good*, as once we are no longer "pending" the wire transfer details will no longer be shown, so the entire screen will look different. So we can probably get rid of the "This transaction is not complete" and "Status: Pending" on WebEx/iOS as that is *always* the case when we give the user wire transfer instructions here!
@@ -648,8 +634,6 @@ Issues
* WebEx(text): not point in showing details if there are no fees.
* iOS(text): not point in showing details if there are no fees.
-* Android(text): still talks about 'exchange'
-* Android(text): has the amount twice, useless without fees
* iOS(text): status: Done is unnecessary, if we show this screen, it will always be 'done' (ok, theoretically in the middle of withdrawing, but the wire transfer will be done; but then maybe show 'incoming' but hide the status once really "done").
* unclear: "Delete" vs. 'Delete from history" --- two
styles, two translations, gain?
@@ -711,8 +695,6 @@ Actions
Issues
^^^^^^
-* Android(text) has button label "OK", should probably be "Open" for uniformity.
-* Android(text) has "Enter Taler URI", while WebEx has clearer text "enter taler:// URI".
* iOS (screenshot): lacks dialog (or screenshot?) entirely, not sure if we need manual URL-entry on mobile though, so could be OK!
@@ -825,7 +807,6 @@ Actions
Issues
^^^^^^
-* Android(text): details say receipt, but WebEx uses the slightly more accurate "Invoice ID". Could also use "Order #"?
* iOS(major): delete button is missing, instead only has "Done"
@@ -947,7 +928,6 @@ Actions
Issues
^^^^^^
-* Android(text): Webex uses "1 Week" instead of "7 days", let's use "week".
* iOS(text): 3 min/ 1h are inconsistent; other wallets have 1 day, 7 days, 30 days. We should be consistent.
diff --git a/design-documents/054-dynamic-form.rst b/design-documents/054-dynamic-form.rst
index d93b3684..5567fc60 100644
--- a/design-documents/054-dynamic-form.rst
+++ b/design-documents/054-dynamic-form.rst
@@ -1,10 +1,13 @@
-DD 54: Dynamic Web Form
-#######################
+DD 54: Dynamic Form
+###################
Summary
=======
-This document outlines the approach for implementing a dynamic web form feature.
+This document outlines the design of forms defined in the
+backend in a JSON file which will be rendered by a client
+for asking information to a person.
+
Motivation
==========
@@ -12,12 +15,16 @@ Motivation
Currently, creating a new form for a web app involves coding a new
page with HTML, CSS, and JS. Exchange AML requires multiple forms,
and different instances may have distinct forms based on jurisdiction.
+Being able to define forms in a JSON file that a client software
+(not just web SPA) could use to ask the information helps to change
+it without requiring a new upgrade of the client app.
Requirements
============
-A form consist of a layout and a set of fields.
+A form consist of a layout, a set of fields and metadata required to
+recognice which form configuration was used to produce the saved value.
Layout requirements
-------------------
@@ -41,159 +48,609 @@ Fields requirements
complex composite structure.
+Metadata requirements
+---------------------
+
+* **identification**: the form configuration instance should have an unique
+ non reusable id.
+
+* **label**: the form should have a name recognizable for the user
+
+* **version**: the same form, with the same id, could be updated. This will
+ increase the version number. A newer form should support older forms.
+
Proposed Solutions
==================
-Forms are initialized using a flexible structure defined by the
-TypeScript interface FormType<T>. This interface comprises properties
-such as value (current form data), initial (initial form data for resetting),
-readOnly (flag to disable input), onUpdate (callback on form data update),
-and computeFormState (function to derive the form state based on current data).
+The propose solution defines the structure of a form, the fields and additional
+type form-configuration which links a form with a set of fields.
+
+Form metadata
+-------------
+
+This is the root object of the configuration.
+.. code-block:: typescript
+
+ type FormMetadata = {
+ label: string;
+ id: string;
+ version: number;
+ config: FormConfiguration;
+ };
+
+
+Form configuration
+------------------
+
+Defies a basic structure and the set of fields the form is going to have.
+
+The ``FormConfiguration`` is an enumerated type which list can be extended in the
+future.
.. code-block:: typescript
- interface FormType<T extends object> {
- value: Partial<T>;
- initial?: Partial<T>;
- readOnly?: boolean;
- onUpdate?: (v: Partial<T>) => void;
- computeFormState?: (v: Partial<T>) => FormState<T>;
+ type FormConfiguration = DoubleColumnForm;
+
+ type DoubleColumnForm = {
+ type: "double-column";
+ design: DoubleColumnFormSection[];
}
+ type DoubleColumnFormSection = {
+ title: string;
+ description?: string;
+ fields: UIFormElementConfig[];
+ };
-``T``: is the type of the result object
-``value``: is a reference to the current value of the result
-``initial``: data for resetting
-``readOnly``: when true, fields won't allow input
-``onUpdate``: notification of the result update
-``computeFormState``: compute a new state of the form based on the current value
-Form state have the same shape of ``T`` but every field type is ``FieldUIOptions``.
+Form fields
+-----------
-Fields type can be:
- * strings
- * numbers
- * boolean
- * arrays
- * object
+A form can have two type of element: decorative/informative or input.
-The field type ``AmountJson`` and ``AbsoluteTime`` are opaque since field is used as a whole.
+An example of a decorative element is a grouping element which make all the fields
+inside the group look into the same section. This element will not allow the user
+to enter information and won't produce any value in the resulting JSON.
-The form can be instanciated using
+An example of an input field is a text field which allows the user to enter text.
+This element should have an ``id`` which will point into the location in which the
+value will be stored in the resulting JSON. Note that two field in the same form
+with the same ``id`` will result in undefined behavior.
+
+The ``UIFormElementConfig`` is an enumerated type with all type of fields supported
+
+.. code-block:: typescript
+
+ type UIFormElementConfig =
+ | UIFormElementGroup
+ | UIFormElementCaption
+ | UIFormFieldAbsoluteTime
+ | UIFormFieldAmount
+ | UIFormFieldArray
+ | UIFormFieldChoiseHorizontal
+ | UIFormFieldChoiseStacked
+ | UIFormFieldFile
+ | UIFormFieldInteger
+ | UIFormFieldSelectMultiple
+ | UIFormFieldSelectOne
+ | UIFormFieldText
+ | UIFormFieldTextArea
+ | UIFormFieldToggle;
+
+
+All form elements should extend from ``UIFieldElementDescription`` which defines a base
+configuration to show a field.
.. code-block:: typescript
- import { FormProvider } from "@gnu-taler/web-util/browser";
+ type UIFieldElementDescription = {
+ /* label if the field, visible for the user */
+ label: string;
+
+ /* long text to be shown on user demand */
+ tooltip?: string;
+
+ /* short text to be shown close to the field, usually below and dimmer*/
+ help?: string;
+
+ /* name of the field, useful for a11y */
+ name: string;
+ /* if the field should be initially hidden */
+ hidden?: boolean;
+
+ };
+
+That will be enough for a decorative form element (like group element or
+a text element) but if it defines an input field then it should extend
+from ``UIFormFieldBaseConfig`` which add more information to the previously
+defined ``UIFieldElementDescription``.
-Then the field component can access all the properties by the ``useField(name)`` hook,
-which will return
.. code-block:: typescript
- interface InputFieldHandler<Type> {
- value: Type;
- onChange: (s: Type) => void;
- state: FieldUIOptions;
- isDirty: boolean;
- }
+ type UIFormFieldBaseConfig = UIFieldElementDescription & {
+ /* example to be shown inside the field */
+ placeholder?: string;
+ /* show a mark as required */
+ required?: boolean;
-``value``: the current value of the field
-``onChange``: a function to call anytime the user want to change the value
-``state``: the state of the field (hidden, error, etc..)
-``isDirty``: if the user already tried to change the value
+ /* readonly and dim */
+ disabled?: boolean;
-A set of common form field exist in ``@gnu-taler/web-util``:
+ /* conversion id to convert the string into the value type
+ the id should be known to the ui impl
+ */
+ converterId?: string;
- * InputAbsoluteTime
- * InputAmount
- * InputArray
- * InputFile
- * InputText
- * InputToggle
+ /* property id of the form */
+ id: UIHandlerId;
+ };
-and should be used inside a ``Form`` context.
+ /**
+ * string which defined a json path
+ *
+ */
+ type UIHandlerId = string
-.. code-block:: none
- function MyFormComponent():VNode {
- return <FormProvider>
- <InputAmount name="amount" />
- <InputText name="subject" />
- <button type="submit"> Confirm </button>
- </FormProvider>
- }
+The ``id`` property defines the location in which this information is going
+to be saved in the JSON result. Formally formally, it should be a ``dot-selector``
+
+.. code-block:: ini
+
+ dot-selector = "." dot-member-name
+ dot-member-name = name-first *name-char
+ name-first = ALPHA / "_"
+ name-char = DIGIT / name-first
+
+ DIGIT = %x30-39 ; 0-9
+ ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+All the input fields will create a string value located where the id
+points, unless a ``convertedId`` is specified. The ``convertedId`` is a reference
+to a converter that the client software implements. For example, an input field
+with ``convertedId = "Taler.Amount"`` will transform the value the user
+entered into a *AmountString* with the currency in the configuration.
+
+
+Description of supported fields
+-------------------------------
+
+All of this fields defines an UI handler which help the user to input
+the value with as handy as possible. The type of the field doesn't define
+the type of the value in the resulting JSON, that's defined by the ``converterId``.
+
+Decorative elements
+```````````````````
+
+To show some additional text
+
+.. code-block:: typescript
+
+ type UIFormElementCaption = { type: "caption" } & UIFieldElementDescription;
+
+To group fields in the UI and maybe show a collapsable handler.
+
+.. code-block:: typescript
+
+ type UIFormElementGroup = {
+ type: "group";
+ fields: UIFormElementConfig[];
+ } & UIFieldElementDescription;
+
Example
---------
+'''''''
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Decorative elements",
+ "fields": [
+ {
+ "type": "caption",
+ "name": "cap",
+ "label": "This is a caption"
+ },
+ {
+ "type": "group",
+ "name": "group",
+ "label": "The first name and last name are in a group",
+ "fields": [
+ {
+ "type": "text",
+ "name": "firstName",
+ "id": ".person.name",
+ "label": "First name"
+ },
+ {
+ "type": "text",
+ "name": "lastName",
+ "id": ".person.lastName",
+ "label": "Last name"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.decorative-elements.png
+ :width: 400
+
+Time input
+``````````
+
+This may be rendered as a calendar
+
+.. code-block:: typescript
+
+ type UIFormFieldAbsoluteTime = {
+ type: "absoluteTimeText";
+ max?: TalerProtocolTimestamp;
+ min?: TalerProtocolTimestamp;
+ pattern: string;
+ } & UIFormFieldBaseConfig;
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Time inputs",
+ "fields": [
+ {
+ "type": "absoluteTime",
+ "name": "thedate",
+ "id": ".birthdate",
+ "converterId": "Taler.AbsoluteTime",
+ "help": "the day you born",
+ "pattern":"dd/MM/yyyy",
+ "label": "Birthdate"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.time.png
+ :width: 400
+
+
+Amount input
+````````````
+
+Money input.
+
+.. code-block:: typescript
+
+ type UIFormFieldAmount = {
+ type: "amount";
+ max?: Integer;
+ min?: Integer;
+ currency: string;
+ } & UIFormFieldBaseConfig;
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Amount inputs",
+ "fields": [
+ {
+ "type": "amount",
+ "name": "thedate",
+ "id": ".amount",
+ "converterId": "Taler.Amount",
+ "help": "how much do you have?",
+ "currency":"EUR",
+ "label": "Amount"
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.amount.png
+ :width: 400
-Consider a form shape represented by the TypeScript type:
+
+List input
+``````````
+
+This input allows to enter more than element in the same field, and the
+resulting JSON will have a json list. The UI should show the elements
+already present in the list, and for that it will use ``labelFieldId``.
.. code-block:: typescript
- type TheFormType = {
- name: string,
- age: number,
- savings: AmountJson,
- nextBirthday: AbsoluteTime,
- pets: string[],
- addres: {
- street: string,
- city: string,
+ type UIFormFieldArray = {
+ type: "array";
+ // id of the field shown when the array is collapsed
+ labelFieldId: UIHandlerId;
+ fields: UIFormElementConfig[];
+ } & UIFormFieldBaseConfig;
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Amount inputs",
+ "fields": [
+ {
+ "type": "array",
+ "name": "people",
+ "id": ".people",
+ "help": "who is coming to the party?",
+ "labelFieldId": ".name",
+ "fields": [{
+ "type": "text",
+ "name": "firstName",
+ "id": ".name",
+ "label": "First name"
+ },
+ {
+ "type": "text",
+ "name": "lastName",
+ "id": ".lastName",
+ "label": "Last name"
+ }],
+ }
+ ]
+ }
+ ]
+ }
}
+
+
+.. image:: ../screenshots/dynamic-forms.list.png
+ :width: 400
+
+Choice input
+````````````
+
+To be used when the user need to choose on predefined values
+
+.. code-block:: typescript
+
+ interface SelectUiChoice {
+ label: string;
+ description?: string;
+ value: string;
}
-An example instance of this form could be:
+A set of buttons next to each other
.. code-block:: typescript
- const theFormValue: TheFormType = {
- name: "Sebastian",
- age: 15,
- pets: ["dog","cat"],
- address: {
- street: "long",
- city: "big",
+ type UIFormFieldChoiseHorizontal = {
+ type: "choiceHorizontal";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+
+A set of buttons next on top of each other
+
+.. code-block:: typescript
+
+ type UIFormFieldChoiseStacked = {
+ type: "choiceStacked";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+A drop down list to select one of the elements
+
+.. code-block:: typescript
+
+ type UIFormFieldSelectOne = {
+ type: "selectOne";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+A drop down list to select multiple of the element, which
+will produce a list of values in the resulting JSON.
+
+.. code-block:: typescript
+
+ type UIFormFieldSelectMultiple = {
+ type: "selectMultiple";
+ max?: Integer;
+ min?: Integer;
+ unique?: boolean;
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Choice inputs",
+ "fields": [
+ {
+ "type": "choiceHorizontal",
+ "name": "food",
+ "label": "Food",
+ "id": ".food",
+ "choices": [
+ {
+ "value": "meat",
+ "label": "Meat"
+ },
+ {
+ "value": "sushi",
+ "label": "Sushi"
+ },
+ {
+ "value": "taco",
+ "label": "Taco"
+ },
+ {
+ "value": "salad",
+ "label": "Salad"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+.. image:: ../screenshots/dynamic-forms.choice.png
+ :width: 400
+
+File input
+``````````
+
+.. code-block:: typescript
+
+ type UIFormFieldFile = {
+ type: "file";
+ maxBytes?: Integer;
+ minBytes?: Integer;
+ // comma-separated list of one or more file types
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept#unique_file_type_specifiers
+ accept?: string;
+ } & UIFormFieldBaseConfig;
+
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "File inputs",
+ "fields": [
+ {
+ "type": "file",
+ "name": "photo",
+ "id": ".photo",
+ "label": "Photo",
+ "accept": "*.png"
+ }
+ ]
+ }
+ ]
}
}
+.. image:: ../screenshots/dynamic-forms.file.png
+ :width: 400
+
+Number input
+````````````
+
+.. code-block:: typescript
+
+ type UIFormFieldInteger = {
+ type: "integer";
+ max?: Integer;
+ min?: Integer;
+ } & UIFormFieldBaseConfig;
+
+
+.. image:: ../screenshots/dynamic-forms.number.png
+ :width: 400
+
+Text input
+``````````
+
+A simple line of text
-For such a form, a valid state can be computed using a function like
-``computeFormStateBasedOnFormValues``, returning an object indicating
-the state of each field, including properties such as ``hidden``,
-``disabled``, and ``required``.
-
-
-.. code-block:: javascript
-
- function computeFormStateBasedOnFormValues(formValues): {
- //returning fixed state as an example
- //the return state will be commonly be computed from the values of the form
- return {
- age: {
- hidden: true,
- },
- pets: {
- disabled: true,
- elements: [{
- disabled: false,
- }],
- },
- address: {
- street: {
- required: true,
- error: "the street name was not found",
- },
- city: {
- required: true,
- },
- },
+.. code-block:: typescript
+
+ type UIFormFieldText = { type: "text" } & UIFormFieldBaseConfig;
+
+A bigger multi-line of text
+
+.. code-block:: typescript
+
+ type UIFormFieldTextArea = { type: "textArea" } & UIFormFieldBaseConfig;
+
+
+.. image:: ../screenshots/dynamic-forms.text.png
+ :width: 400
+
+Boolean input
+`````````````
+
+.. code-block:: typescript
+
+ type UIFormFieldToggle = { type: "toggle" } & UIFormFieldBaseConfig;
+
+
+.. code-block:: json
+
+ {
+ "label": "Example form",
+ "id": "example",
+ "version": 1,
+ "config": {
+ "type": "double-column",
+ "design": [
+ {
+ "title": "Boolean inputs",
+ "fields": [
+ {
+ "type": "toggle",
+ "name": "the_question",
+ "id": ".the_question",
+ "label": "Yes or no?"
+ }
+ ]
+ }
+ ]
}
}
+.. image:: ../screenshots/dynamic-forms.boolean.png
+ :width: 400
+
+Examples
+========
diff --git a/extract-tsdefs/extract.ts b/extract-tsdefs/extract.ts
index 5be82479..5ca6f300 100644
--- a/extract-tsdefs/extract.ts
+++ b/extract-tsdefs/extract.ts
@@ -107,7 +107,7 @@ function gatherDecls(
console.log("string!");
break;
}
- const symbol = type.symbol || type.aliasSymbol;
+ const symbol = type.aliasSymbol || type.symbol;
if (!symbol) {
console.log(`no type symbol for ${node.getText()}`);
break;
@@ -116,7 +116,7 @@ function gatherDecls(
console.log(`symbol name: ${type.symbol?.name}`);
console.log(`alias symbol name: ${type.aliasSymbol?.name}`);
if (perOpState.nameSet.has(name)) {
- console.log("already found!");
+ console.log(`already found ${name}`);
break;
}
perOpState.nameSet.add(name);
@@ -148,6 +148,20 @@ function gatherDecls(
console.log(declText);
break;
}
+ case ts.SyntaxKind.TypeLiteral:
+ if (!type.aliasSymbol) {
+ // Just free-standing type literal, no need to emit!
+ break;
+ }
+ console.log(`got TypeLiteral for ${name}`);
+ const declText = printer.printNode(
+ ts.EmitHint.Unspecified,
+ decl,
+ decl.getSourceFile()!
+ );
+ gatherState.declTexts.set(name, `type ${name} = ${declText};`);
+ console.log(declText);
+ break;
default:
console.log(`unknown decl kind ${ts.SyntaxKind[decl.kind]}`);
break;
@@ -160,20 +174,22 @@ function gatherDecls(
default:
break;
}
- console.log(`syntax children for ${node.getText()}`);
+ console.log(`start syntax children for ${node.getText()}`);
node.forEachChild((child) => {
console.log(`syntax child: ${ts.SyntaxKind[child.kind]}`);
gatherDecls(child, gatherState, perOpState);
});
+ console.log(`end syntax children for ${node.getText()}`);
//console.log(`// unknown node kind ${ts.SyntaxKind[node.kind]}`);
return;
}
function getOpEnumDecl(decl: ts.Declaration): string | undefined {
+ console.log("getting OpEnumDecl")
let enumMemberDecl: undefined | string = undefined;
- function walk(node: ts.Node) {
+ function walk(node: ts.Node, level: number = 0) {
node.forEachChild((x) => {
- console.log(`child kind: ${ts.SyntaxKind[x.kind]}`);
+ console.log(`child kind [${level}]: ${ts.SyntaxKind[x.kind]}`);
console.log(x.getText());
switch (x.kind) {
case ts.SyntaxKind.PropertySignature: {
@@ -185,7 +201,7 @@ function getOpEnumDecl(decl: ts.Declaration): string | undefined {
break;
}
}
- walk(x);
+ walk(x, level + 1);
});
}
walk(decl);
@@ -205,8 +221,13 @@ const main = async () => {
if (!v.name.endsWith("Op")) {
return;
}
+ console.log(`gathering documentation for export ${v.name}`);
const decls = v.getDeclarations();
- decls?.forEach((decl) => {
+ if (!decls) {
+ return;
+ }
+ console.log(`has ${decls.length} declarations`);
+ decls.forEach((decl) => {
console.log(`export decl, kind ${ts.SyntaxKind[decl.kind]}`);
const commentRanges = ts.getLeadingCommentRanges(
diff --git a/images/regional-arch.png b/images/regional-arch.png
deleted file mode 100644
index a3691aea..00000000
--- a/images/regional-arch.png
+++ /dev/null
Binary files differ
diff --git a/images/transaction-withdrawal-states.dot b/images/transaction-withdrawal-states.dot
index 9163c889..4dff520f 100644
--- a/images/transaction-withdrawal-states.dot
+++ b/images/transaction-withdrawal-states.dot
@@ -1,6 +1,7 @@
digraph G {
initial_manual[label="", xlabel="manual" shape="circle"];
initial_bank[label="", xlabel="bank-integrated" shape="circle"];
+ dialog_proposed[label="dialog(proposed)"];
pending_brr[label="bank-register-reserve"];
pending_bc[label="bank-confirm"];
pending_ewr[label="exchange-wait-reserve"];
@@ -25,6 +26,8 @@ digraph G {
}
initial_bank->pending_brr;
+ initial_bank->dialog_proposed;
+ dialog_proposed->pending_brr;
initial_manual->pending_ewr;
pending_brr->pending_bc [color="green"];
pending_brr->aborting_bank [style="dashed", color="blue", label="abort"];
diff --git a/index.rst b/index.rst
index bd3b30d6..417e3a1b 100644
--- a/index.rst
+++ b/index.rst
@@ -18,6 +18,7 @@
@author Sree Harsha Totakura
@author Marcello Stanisci
@author Christian Grothoff
+ @author Javier Sepulveda
GNU Taler Documentation
=======================
@@ -63,6 +64,7 @@ Documentation Overview
taler-auditor-manual
taler-developer-manual
libeufin/index
+ system-administration/index
design-documents/index
global-licensing
manindex
diff --git a/libeufin/index.rst b/libeufin/index.rst
index c77255b0..4fd7668c 100644
--- a/libeufin/index.rst
+++ b/libeufin/index.rst
@@ -31,4 +31,5 @@ LibEuFin is a project providing free software tooling for European FinTech.
bank-manual
regional-automated-manual
regional-custom-manual
+ regional-manual-use
setup-ebics-at-postfinance
diff --git a/libeufin/regional-automated-manual.rst b/libeufin/regional-automated-manual.rst
index f416d1b9..39dfd3f6 100644
--- a/libeufin/regional-automated-manual.rst
+++ b/libeufin/regional-automated-manual.rst
@@ -241,7 +241,6 @@ manual setup and in the the manpage of ``taler-exchange-offline``.
.. include:: ../frags/regional-system-on.rst
.. include:: ../frags/deploying-tos.rst
-.. include:: ../frags/regional-manual-use.rst
Installing Updates
diff --git a/libeufin/regional-custom-manual.rst b/libeufin/regional-custom-manual.rst
index 7da39c96..4a347791 100644
--- a/libeufin/regional-custom-manual.rst
+++ b/libeufin/regional-custom-manual.rst
@@ -141,7 +141,6 @@ account with "CHF".
.. include:: ../frags/regional-system-on.rst
.. include:: ../frags/deploying-tos.rst
-.. include:: ../frags/regional-manual-use.rst
Maintenance
diff --git a/frags/regional-manual-use.rst b/libeufin/regional-manual-use.rst
index 7566d622..ff9f38f8 100644
--- a/frags/regional-manual-use.rst
+++ b/libeufin/regional-manual-use.rst
@@ -1,3 +1,22 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2014-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Florian Dold
+ @author Marcello Stanisci
+ @author Christian Grothoff
+
.. _regional-use:
Using the Regional Currency
diff --git a/manpages/libeufin-nexus.1.rst b/manpages/libeufin-nexus.1.rst
index a0ed99ff..5eb42cab 100644
--- a/manpages/libeufin-nexus.1.rst
+++ b/manpages/libeufin-nexus.1.rst
@@ -93,7 +93,7 @@ Its options are as follows:
Configure logging to use LOGLEVEL.
Uploaded documents will be stored *before* being submitted to the bank. This directory would contain several directories, each named after the ``YYYY-MM-DD/submit`` format. The pain.001 file would then be named in the following schema: ``$microseconds_pain.001.xml``.
**--transient**
- This flag, enabled by default, causes the command to check the database and submit only once, and then return.
+ This flag causes the command to check the database and submit only once, and then return.
ebics-fetch
@@ -105,6 +105,8 @@ The files type can be given as an argument to select what will be fetched. If no
* ``acknowledgement``: EBICS acknowledgement, retrieves the status of EBICS orders.
* ``status``: Payment status, retrieves status of pending debits.
+* ``report``: Account intraday reports, retrieves the history of confirmed debits and credits.
+* ``statement``: Account statements, retrieves the history of confirmed debits and credits.
* ``notification``: Debit & credit notifications, retrieves the history of confirmed debits and credits.
**-h** \| **--help**
@@ -117,10 +119,26 @@ The files type can be given as an argument to select what will be fetched. If no
Log EBICS content at SAVEDIR.
Downloaded documents will be stored *before* being ingested in the database. This directory would contain several directories, each named after the ``YYYY-MM-DD/fetch`` format. The stored files would then be named after the following schema: ``$microseconds_$filename``. Exception to this naming scheme are the HAC responses, since they do not get any filename assigned by the ZIP archive (they are sent unzipped). Their naming scheme is: ``$microseconds_HAC_response.pain.002.xml``.
**--transient**
- This flag, enabled by default, causes the command to perform one download and return.
+ This flag causes the command to perform one download and return.
**--pinned-start**
Only supported in --transient mode, this option lets specify the earliest timestamp of the downloaded documents. The latest timestamp is always the current time.
+serve
+-----
+
+This command starts the HTTP server.
+
+Its options are as follows:
+
+**-h** \| **--help**
+ Print short help on options.
+**-c** \| **--config** *FILENAME*
+ Specifies the configuration file.
+**-L** \| **--log** *LOGLEVEL*
+ Configure logging to use LOGLEVEL.
+**--check**
+ This flag causes the command to check whether an API is in use (if it's useful to start the HTTP server) and to output 0 if at least one API is enabled, otherwise 1.
+
initiate-payment
----------------
diff --git a/manpages/libeufin-nexus.conf.5.rst b/manpages/libeufin-nexus.conf.5.rst
index eb333d1c..8aabe883 100644
--- a/manpages/libeufin-nexus.conf.5.rst
+++ b/manpages/libeufin-nexus.conf.5.rst
@@ -167,6 +167,60 @@ FREQUENCY
IGNORE_TRANSACTIONS_BEFORE
Ignore all transactions before a certain YYYY-MM-DD date, useful when you want to use an existing account with old transactions that should not be bounced.
+HTTP SERVER OPTIONS
+-------------------
+
+The following configuration value(s) belong to the “[nexus-httpd]” section.
+
+SERVE
+ This can either be ``tcp`` or ``unix``.
+
+PORT
+ Port on which the HTTP server listens, e.g. 9967.
+ Only used if ``SERVE`` is ``tcp``.
+
+BIND_TO
+ Which IP address should we bind to? E.g. ``127.0.0.1`` or ``::1``for loopback. Can also be given as a hostname.
+ Only used if ``SERVE`` is ``tcp``.
+
+UNIXPATH
+ Which unix domain path should we bind to?
+ Only used if ``SERVE`` is ``unix``.
+
+UNIXPATH_MODE
+ What should be the file access permissions for ``UNIXPATH``?
+ Only used if ``SERVE`` is ``unix``.
+
+HTTP WIRE GATEWAY API OPTIONS
+-----------------------------
+
+The following configuration value(s) belong to the “[nexus-httpd-wire-gateway-api]” section.
+
+ENABLED
+ Whether to serve the Wire Gateway API.
+
+AUTH_METHOD
+ How to authenticate this API. This can either be ``none`` or ``bearer-token``.
+
+AUTH_BEARER_TOKEN
+ The expected token.
+ Only used if ``AUTH_METHOD`` is ``bearer-token``.
+
+HTTP REVENUE API OPTIONS
+------------------------
+
+The following configuration value(s) belong to the “[nexus-httpd-revenue-api]” section.
+
+ENABLED
+ Whether to serve the Revenue API.
+
+AUTH_METHOD
+ How to authenticate this API. This can either be ``none`` or ``bearer-token``.
+
+AUTH_BEARER_TOKEN
+ The expected token.
+ Only used if ``AUTH_METHOD`` is ``bearer-token``.
+
DATABASE OPTIONS
----------------
diff --git a/manpages/taler.conf.5.rst b/manpages/taler.conf.5.rst
index 3074f68b..8d6350fe 100644
--- a/manpages/taler.conf.5.rst
+++ b/manpages/taler.conf.5.rst
@@ -254,19 +254,29 @@ PRIVACY_ETAG
EXCHANGE KYC PROVIDER OPTIONS
-----------------------------
-The following options must be in the section "[kyc-provider-XXX]" sections.
-
-COST
- Relative cost of the KYC provider, non-negative number.
+The following options must be in the section "[kyc-provider-$PROVIDER_NAME]" sections.
LOGIC
API type of the KYC provider.
-USER_TYPE
- Type of user this provider is for, either INDIVIDUAL or BUSINESS.
+CONVERTER
+ Name of a program to run on the output of the plugin
+ to convert the result into the desired set of attributes.
+ The converter must create a log for the system administrator
+ if the provided inputs do not match expectations.
+ Note that the converter will be expected to output the
+ set of attributes listed under the respective ``[kyc-check-*]``
+ sections. Calling the converter with ``--list-outputs``
+ should generate a (newline-separated) list of attributes
+ the converter promises to generate in its JSON output
+ (when run regularly).
+
+COST
+ Optional cost, useful if clients want to voluntarily
+ trigger authentication procedures for attestation.
-PROVIDED_CHECKS
- List of checks performed by this provider. Space-separated names of checks, must match check names in legitimization rules.
+Additional logic-specific options may be given in the
+section.
EXCHANGE KYC OAUTH2 OPTIONS
@@ -362,6 +372,169 @@ WEBHOOK_AUTH_TOKEN
Authentication token Persona must supply to our webhook. This is an optional setting.
+EXCHANGE KYC CHECK OPTIONS
+--------------------------
+
+The following options must be in "[kyc-check-$CHECK_NAME]" sections.
+
+TYPE
+ Which type of check is this? Also determines
+ the SPA form to show to the user for this check.
+
+ * INFO: wait for staff or contact staff out-of band
+ (only information shown, no SPA action)
+ * FORM: SPA should show an inline (HTML) form
+ * LINK: SPA may start external KYC process or upload
+
+VOLUNTARY
+ Optional. Set to YES to allow this check be
+ done voluntarily by a client (they may then
+ still have to pay for it). Used to offer the
+ SPA to display checks even if they are
+ not required. Default is NO.
+
+PROVIDER_ID
+ Provider id, present only if type is LINK.
+ Refers to a ``kyc-provider-$PROVIDER_ID`` section.
+
+FORM_NAME
+ Name of the SPA form, if type is FORM
+ "INFO" and "LINK" are reserved and must not be used.
+ The exchange server and the SPA must agree on a list
+ of supported forms and the resulting attributes.
+ The SPA should include a JSON resource file
+ "forms.json" mapping form names to arrays of
+ attribute names each form provides.
+ The list of possible FORM names is fixed in the SPA
+ for a particular exchange release.
+
+DESCRIPTION
+ Descriptions to use in the SPA to display the check.
+
+DESCRIPTION_I18N
+ JSON with internationalized descriptions to use
+ in the SPA to display the check.
+
+REQUIRES
+ ';'-separated list of fields that the CONTEXT must
+ provide as inputs to this check. For example,
+ for a FORM of type CHOICE, this might state
+ ``choices: string[];``. The type after the ":"
+ is for now purely for documentation and is
+ not checked. However, it may be shown to AML staff
+ when they configure measures.
+
+OUTPUTS = business_name street city country registration
+ Description of the outputs provided by the check.
+ Basically, the check's output is expected to
+ provide the following fields as attribute inputs into
+ a subsequent AML program.
+ Only given for type FORM; INFO never has any outputs,
+ and for type LINK we can obtain the same information
+ from the CONVERTER via ``--list-outputs``.
+
+FALLBACK
+ Name of an **original** measure to take if the check fails
+ (for any reason, e.g. provider or form fail to
+ satisfy constraints or provider signals user error)
+ Usually should point to a measure that requests
+ AML staff to investigate. The fallback measure
+ context always includes the reasons for the
+ failure.
+
+EXCHANGE KYC RULES
+------------------
+
+The following options must be in "[kyc-rule-$RULE_NAME]" sections.
+
+OPERATION_TYPE = WITHDRAW
+ Operation that triggers this rule.
+ Must be one of WITHDRAW, DEPOSIT, P2P-RECEIVE
+ or WALLET-BALANCE.
+
+NEXT_MEASURES
+ Space-separated list of next measures to be performed.
+ The SPA should display *all* of these measures to the user.
+ (They have a choice of either which ones, or in
+ which order they are to be performed.)
+ A special measure name "verboten" is used if the
+ specified threshold may never be crossed
+ (under this set of rules).
+
+IS_AND_COMBINATOR
+ "YES" if all NEXT_MEASURES will eventually need
+ to be satisfied, "NO" the user has a choice between
+ them. Not actually enforced by the exchange, but
+ primarily used to inform the user whether this is
+ an "and" or "or". YES for "and".
+
+EXPOSED
+ YES if the rule (specifically, operation type,
+ threshold, timeframe) and the general nature of
+ the next measure (verboten or approval required)
+ should be exposed to the client.
+ Defaults to NO if not set.
+
+THRESHOLD
+ Threshold amount above which the rule is
+ triggered. The total must be exceeded in the given
+ timeframe.
+
+TIMEFRAME
+ Timeframe over which the amount to be compared to
+ the THRESHOLD is calculated (for example, "30 days").
+ Ignored for WALLET-BALANCE. Can be 'forever'.
+
+ENABLED = NO
+ Set to YES to enable the rule (default is NO).
+
+
+EXCHANGE AML PROGRAMS
+---------------------
+
+The following options must be in "[aml-program-$PROG_NAME]" sections.
+
+COMMAND
+ Name of the program to run. Must match a binary
+ on the local machine where the exchange is running.
+
+DESCRIPTION
+ Human-readable description of what this
+ AML helper program will do. Used to show
+ to the AML staff.
+
+ENABLED
+ True if this AML program is enabled (and thus can be
+ used in measures and exposed to AML staff).
+ Optional, default is NO.
+
+FALLBACK
+ Name of an **original** measure to take if COMMAND fails
+ Usually points to a measure that asks AML staff
+ to contact the systems administrator. The fallback measure
+ context always includes the reasons for the
+ failure.
+
+EXCHANGE KYC MEASURES
+---------------------
+
+The following options must be in "[kyc-measure-$MEASURE_NAME]" sections. These sections define the **original** measures.
+
+CHECK_NAME
+ Name of a possible check for this measure. Optional.
+ If not given, PROGRAM should be run immediately
+ (on an empty set of attributes).
+
+CONTEXT = {"choices":["individual","business"]}
+ Context for the check. The context can be
+ just an empty JSON object if there is none.
+
+PROGRAM
+ Program to run on the context and check data to
+ determine the outcome and next measure.
+ Refers to a ``[aml-program-$PROG_NAME]`` section name.
+
+
EXCHANGE EXTENSIONS OPTIONS
---------------------------
@@ -564,6 +737,13 @@ PAYTO_URI
``payto://iban/GENODEF1SLR/DE67830654080004822650/`` or
``payto://iban/DE67830654080004822650/`` (providing the BIC is optional).
Note: only the wire-method is actually used from the URI.
+ Alternatively, you can also just specify the wire method.
+
+WIRE_METHOD
+ Specifies the wire method following RFC 8905. Examples:
+ ``x-taler-bank``, ``iban`` or ``wallee``. This option is
+ ignored if a PAYTO_URI is specified, and mandatory if PAYTO_URI is
+ not given.
ENABLE_DEBIT
Must be set to ``YES`` for the accounts that the
diff --git a/python-guidelines.rst b/orphaned/python-guidelines.rst
index 8a644ced..8a644ced 100644
--- a/python-guidelines.rst
+++ b/orphaned/python-guidelines.rst
diff --git a/checklists/qa-0.9.4.rst b/orphaned/qa-0.9.4.rst
index 77e51081..77e51081 100644
--- a/checklists/qa-0.9.4.rst
+++ b/orphaned/qa-0.9.4.rst
diff --git a/screenshots/cta-balance-list-android-1.png b/screenshots/cta-balance-list-android-1.png
new file mode 100644
index 00000000..ea552f77
--- /dev/null
+++ b/screenshots/cta-balance-list-android-1.png
Binary files differ
diff --git a/screenshots/cta-balance-list-android-latest.png b/screenshots/cta-balance-list-android-latest.png
index 90d09afe..6811c9de 120000
--- a/screenshots/cta-balance-list-android-latest.png
+++ b/screenshots/cta-balance-list-android-latest.png
@@ -1 +1 @@
-cta-balance-list-android-0.png \ No newline at end of file
+cta-balance-list-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-payment-paid-android-1.png b/screenshots/cta-payment-paid-android-1.png
new file mode 100644
index 00000000..af6c5633
--- /dev/null
+++ b/screenshots/cta-payment-paid-android-1.png
Binary files differ
diff --git a/screenshots/cta-payment-paid-android-latest.png b/screenshots/cta-payment-paid-android-latest.png
index 8aabd17e..558aa216 120000
--- a/screenshots/cta-payment-paid-android-latest.png
+++ b/screenshots/cta-payment-paid-android-latest.png
@@ -1 +1 @@
-cta-payment-paid-android-0.png \ No newline at end of file
+cta-payment-paid-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-peer-pull-initiate-android-1.png b/screenshots/cta-peer-pull-initiate-android-1.png
new file mode 100644
index 00000000..4109253c
--- /dev/null
+++ b/screenshots/cta-peer-pull-initiate-android-1.png
Binary files differ
diff --git a/screenshots/cta-peer-pull-initiate-android-latest.png b/screenshots/cta-peer-pull-initiate-android-latest.png
index eeb4d8c9..37cf7434 120000
--- a/screenshots/cta-peer-pull-initiate-android-latest.png
+++ b/screenshots/cta-peer-pull-initiate-android-latest.png
@@ -1 +1 @@
-cta-peer-pull-initiate-android-0.png \ No newline at end of file
+cta-peer-pull-initiate-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-transaction-list-android-1.png b/screenshots/cta-transaction-list-android-1.png
new file mode 100644
index 00000000..c3ac2e4f
--- /dev/null
+++ b/screenshots/cta-transaction-list-android-1.png
Binary files differ
diff --git a/screenshots/cta-transaction-list-android-latest.png b/screenshots/cta-transaction-list-android-latest.png
index 9bf9ffa8..f5c83c2c 120000
--- a/screenshots/cta-transaction-list-android-latest.png
+++ b/screenshots/cta-transaction-list-android-latest.png
@@ -1 +1 @@
-cta-transaction-list-android-0.png \ No newline at end of file
+cta-transaction-list-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-url-entry-android-1.png b/screenshots/cta-url-entry-android-1.png
new file mode 100644
index 00000000..4032bf5d
--- /dev/null
+++ b/screenshots/cta-url-entry-android-1.png
Binary files differ
diff --git a/screenshots/cta-url-entry-android-latest.png b/screenshots/cta-url-entry-android-latest.png
index 2cd152f8..053842c6 120000
--- a/screenshots/cta-url-entry-android-latest.png
+++ b/screenshots/cta-url-entry-android-latest.png
@@ -1 +1 @@
-cta-url-entry-android-0.png \ No newline at end of file
+cta-url-entry-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-wire-transfer-android-1.png b/screenshots/cta-wire-transfer-android-1.png
new file mode 100644
index 00000000..0dd0e4b0
--- /dev/null
+++ b/screenshots/cta-wire-transfer-android-1.png
Binary files differ
diff --git a/screenshots/cta-wire-transfer-android-latest.png b/screenshots/cta-wire-transfer-android-latest.png
index f50509ba..38074199 120000
--- a/screenshots/cta-wire-transfer-android-latest.png
+++ b/screenshots/cta-wire-transfer-android-latest.png
@@ -1 +1 @@
-cta-wire-transfer-android-0.png \ No newline at end of file
+cta-wire-transfer-android-1.png \ No newline at end of file
diff --git a/screenshots/cta-withdraw-done-android-1.png b/screenshots/cta-withdraw-done-android-1.png
new file mode 100644
index 00000000..11fc66c5
--- /dev/null
+++ b/screenshots/cta-withdraw-done-android-1.png
Binary files differ
diff --git a/screenshots/cta-withdraw-done-android-latest.png b/screenshots/cta-withdraw-done-android-latest.png
index 7751f9d0..78f631b2 120000
--- a/screenshots/cta-withdraw-done-android-latest.png
+++ b/screenshots/cta-withdraw-done-android-latest.png
@@ -1 +1 @@
-cta-withdraw-done-android-0.png \ No newline at end of file
+cta-withdraw-done-android-1.png \ No newline at end of file
diff --git a/screenshots/dynamic-forms.amount.png b/screenshots/dynamic-forms.amount.png
new file mode 100644
index 00000000..854d901c
--- /dev/null
+++ b/screenshots/dynamic-forms.amount.png
Binary files differ
diff --git a/screenshots/dynamic-forms.boolean.png b/screenshots/dynamic-forms.boolean.png
new file mode 100644
index 00000000..92b1e3bd
--- /dev/null
+++ b/screenshots/dynamic-forms.boolean.png
Binary files differ
diff --git a/screenshots/dynamic-forms.choice.png b/screenshots/dynamic-forms.choice.png
new file mode 100644
index 00000000..5922322f
--- /dev/null
+++ b/screenshots/dynamic-forms.choice.png
Binary files differ
diff --git a/screenshots/dynamic-forms.decorative-elements.png b/screenshots/dynamic-forms.decorative-elements.png
new file mode 100644
index 00000000..4981b2e9
--- /dev/null
+++ b/screenshots/dynamic-forms.decorative-elements.png
Binary files differ
diff --git a/screenshots/dynamic-forms.file.png b/screenshots/dynamic-forms.file.png
new file mode 100644
index 00000000..0895b353
--- /dev/null
+++ b/screenshots/dynamic-forms.file.png
Binary files differ
diff --git a/screenshots/dynamic-forms.list.png b/screenshots/dynamic-forms.list.png
new file mode 100644
index 00000000..0f007254
--- /dev/null
+++ b/screenshots/dynamic-forms.list.png
Binary files differ
diff --git a/screenshots/dynamic-forms.time.png b/screenshots/dynamic-forms.time.png
new file mode 100644
index 00000000..00c8c57c
--- /dev/null
+++ b/screenshots/dynamic-forms.time.png
Binary files differ
diff --git a/system-administration/borgbackup-tutorial.rst b/system-administration/borgbackup-tutorial.rst
new file mode 100644
index 00000000..08a30d6f
--- /dev/null
+++ b/system-administration/borgbackup-tutorial.rst
@@ -0,0 +1,172 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Javier Sepulveda
+
+Borgbackup tutorial
+###################
+
+.. image:: images/borg-logo.png
+ :width: 440
+ :height: 173
+ :alt: borgbackup-logo
+
+Get help
+==========
+borg help
+
+`Borg official documentation <https://borgbackup.readthedocs.io/en/stable/index.html>`_
+
+
+How to install
+==============
+
+The easiest way to install the stable version of Borg is by using the APT package manager method,
+so you stick to the stable package for your distribution, in our case we use Debian.
+
+.. code-block:: console
+
+ # apt install borgbackup
+
+
+Useful command list to use remotely
+======================================
+
+Initiate remote repository. This is somehow really similar of doing a "git init". Borg will create the necessary
+files and structure, for storing your future backups.
+
+.. code-block:: console
+
+ $ mkdir -p /path/to/repo
+ $ borg init -e=none usr@srv:/path/to/repo
+ $ borg init --encryption=repokey usr@srv:/path/to/repo
+
+Send data from your computer to a specif repository
+
+.. code-block:: console
+
+ $ borg create --stats --progress --compression lz4 usr@dest:/path/to/repo::name /path/from/origin
+
+List available backups of specific borg repository (a folder):
+
+.. code-block:: console
+
+ borg list usr@dest:/path/to/repo
+ borg list backups@sam.gnunet.org:/home/backups/$folder
+
+Extract the content to your computer of specific backup:
+
+.. code-block:: console
+
+ borg extract --progress backups@sam.gnunet.org:/home/backups/weblate::2024-04-09T02:00:00
+
+List the content of a specific backup-name
+
+.. code-block:: console
+
+ borg list usr@dest:/path/to/repo::backup-name
+
+
+Find spefic stuff within a backup without the need of downloading and extracting it
+
+.. code-block:: console
+
+ borg list usr@dest:/path/to/repo::backup-name | grep $keyword
+
+
+Restoring data
+=================
+
+.. note::
+
+ Note: For restoring, it is always better to do it from origin, and not in the backup server itself.
+ Please remember this.
+
+.. code-block:: console
+
+ $ mkdir $folder
+ $ cd $folder
+ $ # Find the concrete backup to restore:
+ $ borg list usr@dest:/path/repo
+ $ # Once you have the "name" of the repo
+ $ # You can check the content, to makes sure it has what you are looking for
+ $ borg list usr@dest::/remote/path/backupX
+ $ # Fully extract the backup to destination folder
+ $ borg extract usr@dest::/remote/path/backupX
+ $ # Just extract "some" data
+ $ borg extract usr@dest::/remote/path/backupX path/to/extract/only
+ $ borg extract --progress backups@sam.gnunet.org:/home/backups/weblate::2023-11-13T11:29:30
+
+Free remote disk space
+=========================
+
+Be careful, prune is a potentially dangerous command, it will remove backup archives.
+
+The default of prune is to apply to all archives in the repository unless you restrict its operation to a subset of the archives using --prefix. When using --prefix, be careful to choose a good prefix - e.g. do not use a prefix “foo” if you do not also want to match “foobar”.
+
+It is strongly recommended to always run prune -v --list --dry-run before,
+so you will see what it would do without it actually doing anything.
+
+Examples:
+
+# Keep 7 end of day and 4 additional end of week archives.
+
+borg prune -v --list --dry-run --keep-daily=7 --keep-weekly=4 /path/to/repo
+
+# Real (without dry-run, this will remove data)
+
+borg prune -v --list --keep-daily=7 --keep-weekly=4 /path/to/repo
+
+Full example of how to apply borgbackup in a server, to backup specific data
+============================================================================
+
+You will find quite a few useful scripts to apply borgbackup in the migration-exercise-stable.git private repository,
+and within under the path /taler.net/borgbackup.
+
+The best way and most secure way to use borg is with systemd timers, but it can also be used with a cronjob as usual.
+
+Locations that we use:
+-----------------------
+
+Local folder to gather your files temporarily: /home/user/borgbackup
+
+- borgbackup.sh # to send the data, to the borgbackup server
+- data.sh # to create .tar files, or dump databases
+- borgbackup.service # to execute the borgbackup.sh file
+- borgbackup.timer # to execute the .service file periodically
+
+Once you have tested that the data.sh, and the borgbackup.sh are working properly, copy them to /opt/bin/borgbackup.
+
+For the borgbackup.service and borgbackup.timer files, copy them to: /etc/systemd/system/
+
+.. code-block:: console
+
+ $ cp borgbackup.service borgbackup.timer /etc/systemd/system/
+ $ systemctl daemon-reload
+ $ # Check before the .service file works okay
+ $ systemctl start borgbackup.service
+ $ # Use journalctl -u borgbackup to fix any issue
+ $ systemctl enable --now borgbackup.timer
+
+.. note::
+
+ Once you have finished applying borg in server,it is mandatory in the following days,
+ to check that the system is working properly.
+ Just do a "borg list usr@dest::/remote/path/backupX", to check if a few backups of the previous days,
+ are listed.
+
+
+
+
diff --git a/system-administration/images/borg-logo.png b/system-administration/images/borg-logo.png
new file mode 100644
index 00000000..ab3d089d
--- /dev/null
+++ b/system-administration/images/borg-logo.png
Binary files differ
diff --git a/images/grafana-postgres-exporter.png b/system-administration/images/grafana-postgres-exporter.png
index a51c28f0..a51c28f0 100644
--- a/images/grafana-postgres-exporter.png
+++ b/system-administration/images/grafana-postgres-exporter.png
Binary files differ
diff --git a/images/kuma.png b/system-administration/images/kuma.png
index d98772a1..d98772a1 100644
--- a/images/kuma.png
+++ b/system-administration/images/kuma.png
Binary files differ
diff --git a/system-administration/images/lego-logo.svg b/system-administration/images/lego-logo.svg
new file mode 100644
index 00000000..2b578d34
--- /dev/null
+++ b/system-administration/images/lego-logo.svg
@@ -0,0 +1 @@
+<svg width="538.167" height="152.232" viewBox="0 0 142.39 40.278" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="none" stroke="#00add8" stroke-width="2.646"><path d="M129.04 6.615c-6.952 0-6.952 4.973-6.952 6.024V27.61c0 .62 0 6.053 6.952 6.053s6.735-5.423 6.735-6.053V12.64c0-1.013.217-6.024-6.735-6.024z"/><path d="M113.61 12.639c0-1.013.217-6.025-6.735-6.025s-6.952 4.973-6.952 6.025V27.61c0 .62 0 6.053 6.952 6.053s6.735-5.423 6.735-6.053v-7.465h-4.53" stroke-linecap="square"/></g><g fill="none" stroke="#00add8" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.323"><path d="M88.866 31.356v-3.374c0-1.472-.874-2.83-2.724-2.83H81.05m5.509 8.511a2.307 2.307 0 1 0 4.614 0 2.307 2.307 0 0 0-4.614 0zM86.559 20.145h-5.551m5.551 0a2.307 2.307 0 1 0 4.614 0 2.307 2.307 0 0 0-4.614 0zM88.866 8.922v3.374c0 1.472-.874 2.83-2.724 2.83H81.05m5.509-8.511a2.307 2.307 0 1 0 4.614 0 2.307 2.307 0 0 0-4.614 0z"/></g><path d="M62.737 13.728V9.291c-.001-3.22 2.772-5.887 5.993-5.889 3.221-.002 5.997 2.662 6 5.883.002 3.22 0 4.443 0 4.443" fill="none" stroke="#4db969" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.133" style="paint-order:fill markers stroke"/><rect x="60.158" y="13.728" width="17.047" height="12.13" ry="1.725" fill="#4db969" stroke="#4db969" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.117" style="paint-order:normal"/><g fill="#fff" stroke-width=".146"><path class="cls-4" d="M66.397 21.903a.414.414 0 0 0 .358-.206l.358-.62.285-.494.015-.025.906-1.571a.414.414 0 0 1 .717 0l.61 1.055a.412.412 0 1 0 .716-.412l-1.326-2.297a.414.414 0 0 0-.717 0l-2.28 3.947a.414.414 0 0 0 .358.623z"/><path class="cls-4" d="M73.172 22.73h-8.207a.414.414 0 0 1-.358-.62l3.713-6.432a.414.414 0 0 1 .716 0l2.759 4.774a.414.414 0 0 1-.358.62h-3.129a.412.412 0 1 0 0 .826h4.563a.414.414 0 0 0 .358-.62l-3.865-6.695a.414.414 0 0 0-.358-.208h-.652a.414.414 0 0 0-.359.208l-4.492 7.781a.411.411 0 0 0 0 .414l.326.564a.41.41 0 0 0 .357.207h8.987a.41.41 0 0 0 .357-.207.412.412 0 0 0-.358-.612zM73.226 19.629l.868 1.503a.412.412 0 1 0 .715-.414l-.868-1.501a.414.414 0 0 0-.715.412zM70.555 15.003l.284.491a.412.412 0 1 0 .715-.412l-.283-.49a.414.414 0 0 0-.716.411zM71.793 17.147l.478.829a.414.414 0 0 0 .716-.414l-.478-.829a.414.414 0 0 0-.716.414zM72.217 24.384h-.981a.414.414 0 0 0 0 .827h.98a.412.412 0 0 0 .357-.62.413.413 0 0 0-.356-.207zM69.327 24.384a.414.414 0 1 0 .001.828.414.414 0 0 0-.001-.828zM65.564 17.146l1.237-2.143a.414.414 0 0 0-.717-.412l-1.236 2.141a.414.414 0 1 0 .716.414zM63.269 21.132l1.346-2.332a.412.412 0 1 0-.715-.414l-1.346 2.332a.412.412 0 1 0 .715.414zM67.418 24.384h-2.28a.414.414 0 0 0 .002.827h2.278a.415.415 0 0 0 .358-.62.415.415 0 0 0-.358-.207z"/></g><g fill="none" stroke="#f9a11d" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.323"><path d="M48.523 31.356v-3.374c0-1.472.874-2.83 2.724-2.83h5.092m-5.509 8.511a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0zM50.83 20.145h5.551m-5.551 0a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0zM48.523 8.922v3.374c0 1.472.874 2.83 2.724 2.83h5.092M50.83 6.614a2.307 2.307 0 1 1-4.614 0 2.307 2.307 0 0 1 4.614 0z"/></g><g fill="none" stroke="#f9a11d" stroke-linecap="square"><path d="M34.821 20.145H24.104m13.285 13.518H24.104V6.614h13.285" stroke-width="2.646"/><path d="M6.615 33.663h10.9M6.615 6.614v27.049m0-27.049v27.049" stroke-width="2.381"/></g></svg>
diff --git a/images/taler-monitoring-infrastructure.png b/system-administration/images/taler-monitoring-infrastructure.png
index 05f29704..05f29704 100644
--- a/images/taler-monitoring-infrastructure.png
+++ b/system-administration/images/taler-monitoring-infrastructure.png
Binary files differ
diff --git a/images/uptime-kuma-edit.png b/system-administration/images/uptime-kuma-edit.png
index 23b85dad..23b85dad 100644
--- a/images/uptime-kuma-edit.png
+++ b/system-administration/images/uptime-kuma-edit.png
Binary files differ
diff --git a/images/uptime-kuma-from-grafana.png b/system-administration/images/uptime-kuma-from-grafana.png
index c42b8660..c42b8660 100644
--- a/images/uptime-kuma-from-grafana.png
+++ b/system-administration/images/uptime-kuma-from-grafana.png
Binary files differ
diff --git a/system-administration/index.rst b/system-administration/index.rst
new file mode 100644
index 00000000..b87c9289
--- /dev/null
+++ b/system-administration/index.rst
@@ -0,0 +1,27 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Javier Sepulveda
+
+System Administration tutorials
+##################################
+
+.. toctree::
+ :maxdepth: 1
+ :glob:
+
+ lego-certificates
+ taler-monitoring-infrastructure
+ borgbackup-tutorial
diff --git a/system-administration/lego-certificates.rst b/system-administration/lego-certificates.rst
new file mode 100644
index 00000000..26ffaf34
--- /dev/null
+++ b/system-administration/lego-certificates.rst
@@ -0,0 +1,144 @@
+..
+ This file is part of GNU TALER.
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 2.1, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author Javier Sepulveda
+
+.. image:: images/lego-logo.svg
+ :width: 300
+ :height: 150
+ :alt: lego logo
+
+What is Lego
+############
+
+Let's Encrypt client and ACME library written in Go.
+
+* You can request new certificates
+* You can request new subdomain alt names for your current main certicate
+* You can renew certificates
+* You can revoke certificates
+* You can request certificates by using dynamic DNS (API access, with multiple providers)
+
+
+Why lego is better for managing certificates
+============================================
+
+* The process is not considered a live process, so in case something goes wrong your websites won't break.
+* You can hook some actions after the renewal process, such as reloading Dovecot.
+* The process of either obtaining or renewing new certicates, doesn't require you to stop NGINX.
+* Lego just helps you to obtain the certificates as text files, which you can copy afterwards to the right locations to be used by NGINX.
+
+
+Requirements
+============
+- A fully automation of installing and deploying Lego can be found in migration-exercise-stable.git/taler.net/lego-certificates
+- If you want to do things manually instead, you can execute the "install-lego.sh" file.
+- To use our script simply execute the "main-certs.sh" file, which not only will install lego on your system, but
+ will try to obtain certificates for the ones listed on the "domains" text file.
+- Lego can work with so many domain providers (dynamic DNS), so please make sure you have indicated the right
+ API credentials on the "envars" variables file for your domain provider. In our specific case, we use Joker.
+- Make sure either you are not using UFW or any firewall program, or that if you are using one, make sure you have opened beforehand
+ the port 80.
+
+Installation and deployment with a script
+=========================================
+
+#. Git clone migration-exercise-stable.git
+#. Navigate to the folder taler.net/lego-certificates
+#. Add your desired FQDNs in the "domains" text file
+#. Execute the "main-certs.sh" file as ./main-certs.sh
+
+Manually installing Lego
+===========================
+
+.. note ::
+ Just as an informative process, as this is fully automated by executing either the "install-lego.sh" or the "main-certs.sh" files.
+
+.. code-block:: console
+
+ $ wget https://github.com/go-acme/lego/releases/download/v4.16.1/lego_v4.16.1_linux_amd64.tar.gz
+ $ tar -axf lego_v4.16.1_linux_amd64.tar.gz
+ $ # If moving directly to /usr/local/bin, just copy the lego binary file to /usr/local/bin
+ $ cp /tmp/lego /usr/local/bin/
+ $ # If copying the binary to /opt/lego, make symbolic links to /usr/local/bin
+ $ cp /tmp/lego /opt/lego/
+ $ ln -s /usr/local/bin /opt/lego/lego
+
+Full documentation on how to use Lego can be found in: https://go-acme.github.io/lego/
+
+Usage of lego once it has been installed
+========================================
+
+* Each time you want to add an additional domain to your setup, just add the FQDN to the "domains" text file
+* There is nothing else to do in your side now, the server itself will trigger automatically (systemd timer) the "renew-certs.service"
+* We have implemented the use of lego with systemd timers, so there is not additional maintenance
+
+Automatic renewal of certificates
+=================================
+
+We use systemd timers do undertake this.
+
+.. note ::
+ To check the systemd timer is running properly and "waiting", you can execute "systemctl status renew-certs.timer"
+
+More information: https://go-acme.github.io/lego/usage/cli/renew-a-certificate/
+
+
+Email notifications
+====================
+
+* Let's encrypt notifications will arrive to your configured email address.
+* You can specify your email address by editing the "envars" text file (variable "LEGO_ACCOUNT_EMAIL").
+* On each successful renewal, you will receive an email notification from the script.
+
+Additional information for troubleshooting
+==========================================
+
+Once you have the certificate generated files (/root/.lego/xxx.crt, /root/.lego/xxx.key)
+they will be copied to /etc/ssl/certs and /etc/ssl/private, respectively.
+
+How to configure NGINX to use your certificates
+===============================================
+
+In the NGINX virtualhost configuration file just include "include conf.d/talerssl.conf;" line, and
+make sure you have a file named "talerssl.conf" in the path: /etc/nginx/conf.d with the next content:
+
+.. code-block:: console
+
+ $ # Taler SSL defaults
+ $ # We're using one certificate with taler.net as primary name
+ $ # and everything else as alt name.
+ $ # These 2 next lines are the important ones, which refer to the certificates file (.crt), and its private key (.key)
+ $ ssl_certificate /etc/ssl/certs/taler.net.crt;
+ $ ssl_certificate_key /etc/ssl/private/taler.net.key;
+ $ ssl_session_cache shared:SSL:10m;
+ $ ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
+ $ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
+
+
+Presence of Lego in our servers
+===============================
+
+* TUE - University of Eindhoven
+
+
+
+
+
+
+
+
+
+
diff --git a/taler-monitoring-infrastructure.rst b/system-administration/taler-monitoring-infrastructure.rst
index 3b809fb3..e1b26c3b 100644
--- a/taler-monitoring-infrastructure.rst
+++ b/system-administration/taler-monitoring-infrastructure.rst
@@ -24,14 +24,15 @@ GNU Taler monitoring
In order to check the availability of our server infrastructure, we use the Grafana and Uptime KUMA monitoring programs.
-On the one hand Grafana let us to see *graphically* the server consumption resources, and even alert us of some specific situations.
+On the one hand Grafana enables us to see *graphically* the server consumption resources, and even alert us of some specific situations.
On the other hand with a more basic tool such as Uptime KUMA (which does mostly ping and https checks),
-we get the very first status information, as the very first countermeasure.
+we get the very first status information, as a very first countermeasure.
Grafana
=======
- Our grafana instance can be reached at https://grafana.taler.net
+- Our grafana instance is installed on the (TUE) server
User accounts:
--------------
@@ -162,19 +163,22 @@ Grafana Alerting
Uptime Kuma
===========
-- URL: http://139.162.254.179:3001/dashboard
+- URL: https://uptimekuma.anastasis.lu (main)
- Users: One single administration account with full privileges.
-- Installation: With Docker
+- Installation: Without docker. All within the user home folder /home/uptime-kuma
+- Monitors almost all our servers, websites and certificates expiration dates.
+
+- URL: https://uptimekuma.taler.net
+- Users: One single administration account with full privileges.
+- Installation: Without docker. All within the user home folder /home/uptime-kuma
+- Monitors the "main" uptimekuma installation, to make sure it is up and running, and doing the monitoring properly.
.. image:: images/kuma.png
.. note::
- 1) In order to guarantee the KUMA is doing its work, it needs to be install 100% externally from the servers you want to monitor. (Server Kuma 1)
- 2) Also, it is important to monitor the KUMA server itself, so you don't endup without a monitoring system. (Server Kuma 2)
-
-In our case, we do both. We have the two Uptime KUMA servers completely outside our server infrastructure, so one monitors the other, and
-the latter one, monitors our own Taler servers.
+ 1) The main uptimekuma installation is under the server anastasis.lu
+ 2) The second uptimekuma installation on top, is installed on gv.taler.net.
Kuma monitor types
-------------------
@@ -187,9 +191,10 @@ expiration dates.
So in brief in our KUMA main server, we use these 3 monitor types (ping,https,certificate expiration) for each website that we monitor.
-Exceptionally for additional notifications, and specifically due of the importance of the Taler Operations server,
-we use in addition SMS notifications (clicksend provider). This way in case of KUMA detecting the Taler Operations unavailability,
-a SMS message will be sent to at the very least two persons from the deployment and operations department.
+Exceptionally for high priority notifications for essential services, and specifically due of the importance of the Taler Operations production
+server, we use in addition SMS notifications (Clicksend provider). This way in the case the main uptimekuma detecting the Taler Operations server unavailability, or any other essential service such as GIt,
+a SMS message would be sent to the system administrator and eventually some other team member of the deployment and operations department, for urgent action.
+
How to edit notifications:
diff --git a/taler-developer-manual.rst b/taler-developer-manual.rst
index 3035e55d..c225e747 100644
--- a/taler-developer-manual.rst
+++ b/taler-developer-manual.rst
@@ -368,12 +368,6 @@ you can add the nightly package sources.
$ wget -O - https://taler.net/taler-systems-nightly.gpg.key | apt-key add -
-Language-Specific Guidelines
-============================
-
-* :doc:`Python Guidelines <python-guidelines>`
-
-
Taler Deployment on gv.taler.net
================================
@@ -648,7 +642,7 @@ outside of this versioning. All tables of a GNU Taler component should live in
QA Plans
========
-.. include:: checklists/qa-0.9.4.rst
+.. include:: checklists/qa-0.10.rst
Releases
diff --git a/taler-exchange-manual.rst b/taler-exchange-manual.rst
index 91865f24..42ec9861 100644
--- a/taler-exchange-manual.rst
+++ b/taler-exchange-manual.rst
@@ -1023,6 +1023,20 @@ Taler KYC Terminology
*cost*. Interaction with a provider is performed by provider-specific
*logic*.
+Configuration of possible KYC/AML checks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The configuration specifies a set of possible KYC checks offered by external
+providers. The names of the configuration sections must being with
+``kyc-check-`` followed by an arbitrary ``$CHECK_NAME``.
+
+The list of possible FORM names is fixed in the SPA
+for a particular exchange release.
+
+The outcome of *any* check should always be uploaded encrypted into the
+``kyc_attributes`` table. It MUST include an ``expiration_time``.
+
+
KYC Configuration Options
-------------------------
@@ -1724,6 +1738,155 @@ from ``https://git.taler.net/wallet-core.git/``, compile and copy the file
from the ``dist/prod``.
+AML Programs
+------------
+
+AML programs are helper programs that can:
+
+* Generate a list of *required* context field names
+ for the helper (introspection!) using the "--required-context"
+ command-line switch. The output should use the same
+ syntax as the REQUIRES clause of ``[kyc-check-]``
+ configuration sections, except that new lines
+ MUST be used to separate fields instead of ";".
+* Generate a list of *required* attribute names
+ for the helper (introspection!) using the "--required-attributes"
+ command-line switch. The output should use the same
+ list of names as the ATTRIBUTES in the
+ ``[kyc-provider-]`` configuration section
+ (but may also include FORM field names).
+* Process an input JSON object of type
+ `AmlProgramInput` into a JSON object of
+ type `AmlOutcome`.
+ This is the default behavior if no command-line switches
+ are provided.
+
+.. ts:def:: AmlProgramInput
+
+ interface AmlProgramInput {
+
+ // JSON object that was provided as
+ // part of the *measure*. This JSON object is
+ // provided under "context" in the main JSON object
+ // input to the AML program. This "context" should
+ // satify both the REQUIRES clause of the respective
+ // check and the output of "--requires" from the
+ // AML program's command-line option.
+ context?: Object;
+
+ // JSON object that captures the
+ // output of a ``[kyc-provider-]`` or (HTML) FORM.
+ // The keys in the JSON object will be the attribute
+ // names and the values must be strings representing
+ // the data. In the case of file uploads, the data
+ // MUST be base64-encoded.
+ attributes: Object;
+
+ // JSON array with the results of historic
+ // AML desisions about the account.
+ aml_history: AmlDecisionDetail[];
+
+ // JSON array with the results of historic
+ // KYC data about the account.
+ kyc_history: KycDetail[];
+
+ }
+
+.. ts:def:: AmlOutcome
+
+ interface AmlOutcome {
+
+ // Should the client's account be investigated
+ // by AML staff?
+ // Defaults to false.
+ to_investigate?: boolean;
+
+ // Free-form properties about the account.
+ // Can be used to store properties such as PEP,
+ // risk category, type of business, hits on
+ // sanctions lists, etc.
+ properties?: AccountProperties;
+
+ // Types of events to add to the KYC events table.
+ // (for statistics).
+ events?: string[];
+
+ // KYC rules to apply. Note that this
+ // overrides *all* of the default rules
+ // until the ``expiration_time`` and specifies
+ // the successor measure to apply after the
+ // expiration time.
+ new_rules: LegitimizationRuleSet;
+
+ }
+
+If the AML program fails (exits with a failure code or
+does not provide well-formed JSON output) the AML/KYC
+process continues with the FALLBACK measure. This should
+usually be one that asks AML staff to contact the
+systems administrator.
+
+AML programs are listed in the configuration file, one program per section:
+
+.. code-block:: ini
+
+ [aml-program-$PROG_NAME]
+
+ # Program to run.
+ COMMAND = taler-helper-aml-pep
+
+ # Human-readable description of what this
+ # AML helper program will do. Used to show
+ # to the AML staff.
+ DESCRIPTION = "check if the customer is a PEP"
+
+ # True if this AML program is enabled (and thus can be
+ # used in measures and exposed to AML staff).
+ # Optional, default is NO.
+ ENABLED = YES
+
+ # **original** measure to take if COMMAND fails
+ # Usually points to a measure that asks AML staff
+ # to contact the systems administrator. The fallback measure
+ # context always includes the reasons for the
+ # failure.
+ FALLBACK = MEASURE_NAME
+
+AML Measures
+------------
+
+The exchange configuration specifies a set of
+**original** *measures* one per configuration section:
+
+.. code-block:: ini
+
+ [kyc-measure-$MEASURE_NAME]
+
+ # Possible check for this measure. Optional.
+ # If not given, PROGRAM should be run immediately
+ # (on an empty set of attributes).
+ CHECK_NAME = IB_FORM
+
+ # Context for the check. The context can be
+ # just an empty JSON object if there is none.
+ CONTEXT = {"choices":["individual","business"]}
+
+ # Program to run on the context and check data to
+ # determine the outcome and next measure.
+ PROGRAM = taler-aml-program
+
+If no ``CHECK_NAME`` is provided at all, the AML ``PROGRAM`` is to be run
+immediately. This is useful if no client-interaction is required to arrive at
+a decision.
+
+.. note::
+
+ The list of *measures* is not complete: AML staff may freely define new
+ measures dynamically, usually by selecting checks, an AML program, and
+ providing context.
+
+
+
Setup Linting
=============
@@ -1902,6 +2065,26 @@ The database scheme used by the exchange looks as follows:
.. image:: images/exchange-db.png
+The ``jmeasures`` JSON in the ``legitimization_measures``
+table is of type `LegitimizationMeasures`:
+
+.. ts:def:: LegitimizationMeasures
+
+ interface LegitimizationMeasures {
+
+ // Array of legitimization measures that
+ // are to be applied.
+ measures: MeasureInformation[];
+
+ // True if the client is expected to eventually satisfy all requirements.
+ // Default (if missing) is false.
+ is_and_combinator?: boolean;
+
+ // True if the requested operation is categorically forbidden.
+ // The measures array will be empty in this case.
+ verboten: boolean;
+ }
+
.. _Database-upgrades:
diff --git a/taler-merchant-manual.rst b/taler-merchant-manual.rst
index 353885cd..c647d3a2 100644
--- a/taler-merchant-manual.rst
+++ b/taler-merchant-manual.rst
@@ -635,11 +635,9 @@ section, the following options need to be configured:
Note that multiple exchanges can be added to the system by using different
-identifiers in place of ``KUDOS`` in the example above. Note that all of the
-exchanges actually used will use the same currency: If the currency does not
-match the main ``CURRENCY`` option from the ``taler`` section, the respective
-``merchant-exchange-`` section is automatically ignored. If you need support
-for multiple currencies, you need to deploy one backend per currency.
+identifiers in place of ``KUDOS`` in the example above. One exchange will only
+ever support a single currency; thus, if you need support for multiple
+currencies, you must add multiple exchanges.
The merchant already ships with a default configuration that contains the
``merchant-exchange-kudos`` section from above.
diff --git a/taler-merchant-pos-terminal.rst b/taler-merchant-pos-terminal.rst
index efc487cb..50e7ff97 100644
--- a/taler-merchant-pos-terminal.rst
+++ b/taler-merchant-pos-terminal.rst
@@ -106,19 +106,6 @@ The elements of the JSON file are defined as follows:
api_key: string;
}
- .. ts:def:: MerchantCategory
-
- interface MerchantCategory {
- // A unique numeric ID of the category
- id: number;
-
- // The name of the category. This will be shown to users and used in the order summary.
- name: string;
-
- // Map from IETF BCP 47 language tags to localized names
- name_i18n?: { [lang_tag: string]: string };
- }
-
.. ts:def:: MerchantProduct
diff --git a/wallet/wallet-core.md b/wallet/wallet-core.md
index 8c600081..b3ea3426 100644
--- a/wallet/wallet-core.md
+++ b/wallet/wallet-core.md
@@ -3,8 +3,10 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
## Overview
### Unknown Group
* [InitWalletOp](#initwalletop)
+* [ShutdownOp](#shutdownop)
* [SetWalletRunConfigOp](#setwalletrunconfigop)
* [GetVersionOp](#getversionop)
+* [HintNetworkAvailabilityOp](#hintnetworkavailabilityop)
### Basic Wallet Information
* [GetBalancesOp](#getbalancesop)
* [GetBalancesDetailOp](#getbalancesdetailop)
@@ -30,11 +32,14 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
### Withdrawals
* [GetWithdrawalDetailsForAmountOp](#getwithdrawaldetailsforamountop)
* [GetWithdrawalDetailsForUriOp](#getwithdrawaldetailsforuriop)
+* [PrepareBankIntegratedWithdrawalOp](#preparebankintegratedwithdrawalop)
+* [ConfirmWithdrawalOp](#confirmwithdrawalop)
* [AcceptBankIntegratedWithdrawalOp](#acceptbankintegratedwithdrawalop)
* [AcceptManualWithdrawalOp](#acceptmanualwithdrawalop)
### Merchant Payments
* [PreparePayForUriOp](#preparepayforuriop)
* [SharePaymentOp](#sharepaymentop)
+* [CheckPayForTemplateOp](#checkpayfortemplateop)
* [PreparePayForTemplateOp](#preparepayfortemplateop)
* [GetContractTermsDetailsOp](#getcontracttermsdetailsop)
* [ConfirmPayOp](#confirmpayop)
@@ -92,6 +97,7 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
* [ConfirmPeerPullDebitOp](#confirmpeerpulldebitop)
### Data Validation
* [ValidateIbanOp](#validateibanop)
+* [CanonicalizeBaseUrlOp](#canonicalizebaseurlop)
### Database Management
* [ExportDbOp](#exportdbop)
* [ImportDbOp](#importdbop)
@@ -111,9 +117,11 @@ This file is auto-generated from [wallet-core](https://git.taler.net/wallet-core
* [TestingSetTimetravelOp](#testingsettimetravelop)
* [TestingListTasksForTransactionOp](#testinglisttasksfortransactionop)
* [TestingWaitTransactionsFinalOp](#testingwaittransactionsfinalop)
+* [TestingWaitTasksDoneOp](#testingwaittasksdoneop)
* [TestingWaitRefreshesFinalOp](#testingwaitrefreshesfinalop)
* [TestingWaitTransactionStateOp](#testingwaittransactionstateop)
* [TestingPingOp](#testingpingop)
+* [TestingGetReserveHistoryOp](#testinggetreservehistoryop)
* [TestingGetDenomStatsOp](#testinggetdenomstatsop)
* [SetCoinSuspendedOp](#setcoinsuspendedop)
* [ForceRefreshOp](#forcerefreshop)
@@ -134,6 +142,17 @@ export type InitWalletOp = {
```
+### ShutdownOp
+```typescript
+export type ShutdownOp = {
+ op: WalletApiOperation.Shutdown;
+ request: EmptyObject;
+ response: EmptyObject;
+};
+// Shutdown = "shutdown"
+
+```
+
### SetWalletRunConfigOp
```typescript
/**
@@ -161,6 +180,23 @@ export type GetVersionOp = {
```
+### HintNetworkAvailabilityOp
+```typescript
+export type HintNetworkAvailabilityOp = {
+ op: WalletApiOperation.HintNetworkAvailability;
+ request: HintNetworkAvailabilityRequest;
+ response: EmptyObject;
+};
+// HintNetworkAvailability = "hintNetworkAvailability"
+
+```
+```typescript
+export interface HintNetworkAvailabilityRequest {
+ isNetworkAvailable: boolean;
+}
+
+```
+
### GetBalancesOp
```typescript
/**
@@ -645,6 +681,18 @@ export interface GetWithdrawalDetailsForAmountRequest {
exchangeBaseUrl: string;
amount: AmountString;
restrictAge?: number;
+ /**
+ * ID provided by the client to cancel the request.
+ *
+ * If the same request is made again with the same clientCancellationId,
+ * all previous requests are cancelled.
+ *
+ * The cancelled request will receive an error response with
+ * an error code that indicates the cancellation.
+ *
+ * The cancellation is best-effort, responses might still arrive.
+ */
+ clientCancellationId?: string;
}
```
@@ -710,28 +758,63 @@ export type GetWithdrawalDetailsForUriOp = {
```typescript
export interface GetWithdrawalDetailsForUriRequest {
talerWithdrawUri: string;
+ /**
+ * @deprecated not used
+ */
restrictAge?: number;
- notifyChangeFromPendingTimeoutMs?: number;
}
```
+
+### PrepareBankIntegratedWithdrawalOp
```typescript
-export interface WithdrawUriInfoResponse {
- operationId: string;
- status: WithdrawalOperationStatus;
- confirmTransferUrl?: string;
- amount: AmountString;
- defaultExchangeBaseUrl?: string;
- possibleExchanges: ExchangeListItem[];
+/**
+ * Prepare a bank-integrated withdrawal operation.
+ */
+export type PrepareBankIntegratedWithdrawalOp = {
+ op: WalletApiOperation.PrepareBankIntegratedWithdrawal;
+ request: PrepareBankIntegratedWithdrawalRequest;
+ response: PrepareBankIntegratedWithdrawalResponse;
+};
+// PrepareBankIntegratedWithdrawal = "prepareBankIntegratedWithdrawal"
+
+```
+```typescript
+export interface PrepareBankIntegratedWithdrawalRequest {
+ talerWithdrawUri: string;
+ selectedExchange?: string;
}
```
```typescript
-export type WithdrawalOperationStatus =
- | "pending"
- | "selected"
- | "aborted"
- | "confirmed";
+export interface PrepareBankIntegratedWithdrawalResponse {
+ transactionId?: string;
+ info: WithdrawUriInfoResponse;
+}
+
+```
+
+### ConfirmWithdrawalOp
+```typescript
+/**
+ * Confirm a withdrawal transaction.
+ */
+export type ConfirmWithdrawalOp = {
+ op: WalletApiOperation.ConfirmWithdrawal;
+ request: ConfirmWithdrawalRequest;
+ response: EmptyObject;
+};
+// ConfirmWithdrawal = "confirmWithdrawal"
+
+```
+```typescript
+export interface ConfirmWithdrawalRequest {
+ transactionId: string;
+ exchangeBaseUrl: string;
+ amount: AmountString;
+ forcedDenomSel?: ForcedDenomSel;
+ restrictAge?: number;
+}
```
@@ -739,6 +822,8 @@ export type WithdrawalOperationStatus =
```typescript
/**
* Accept a bank-integrated withdrawal.
+ *
+ * @deprecated in favor of prepare/confirm withdrawal.
*/
export type AcceptBankIntegratedWithdrawalOp = {
op: WalletApiOperation.AcceptBankIntegratedWithdrawal;
@@ -784,6 +869,14 @@ export interface AcceptManualWithdrawalRequest {
exchangeBaseUrl: string;
amount: AmountString;
restrictAge?: number;
+ /**
+ * Instead of generating a fresh, random reserve key pair,
+ * use the provided reserve private key.
+ *
+ * Use with caution. Usage of this field may be restricted
+ * to developer mode.
+ */
+ forceReservePriv?: EddsaPrivateKeyString;
}
```
@@ -849,6 +942,59 @@ export interface SharePaymentResult {
```
+### CheckPayForTemplateOp
+```typescript
+export type CheckPayForTemplateOp = {
+ op: WalletApiOperation.CheckPayForTemplate;
+ request: CheckPayTemplateRequest;
+ response: CheckPayTemplateReponse;
+};
+// CheckPayForTemplate = "checkPayForTemplate"
+
+```
+```typescript
+export interface CheckPayTemplateRequest {
+ talerPayTemplateUri: string;
+}
+
+```
+```typescript
+export type CheckPayTemplateReponse = {
+ templateDetails: TalerMerchantApi.WalletTemplateDetails;
+ supportedCurrencies: string[];
+};
+
+```
+```typescript
+export interface WalletTemplateDetails {
+ template_contract: TemplateContractDetails;
+ editable_defaults?: TemplateContractDetailsDefaults;
+ required_currency?: string;
+}
+
+```
+```typescript
+export interface TemplateContractDetails {
+ summary?: string;
+ currency?: string;
+ amount?: AmountString;
+ minimum_age: Integer;
+ pay_duration: RelativeTime;
+}
+
+```
+```typescript
+export interface TemplateContractDetailsDefaults {
+ summary?: string;
+ currency?: string;
+ /**
+ * Amount *or* a plain currency string.
+ */
+ amount?: string;
+}
+
+```
+
### PreparePayForTemplateOp
```typescript
/**
@@ -869,6 +1015,13 @@ export interface PreparePayTemplateRequest {
}
```
+```typescript
+export type TemplateParams = {
+ amount?: string;
+ summary?: string;
+};
+
+```
### GetContractTermsDetailsOp
```typescript
@@ -882,7 +1035,8 @@ export type GetContractTermsDetailsOp = {
```
```typescript
export interface GetContractTermsDetailsRequest {
- proposalId: string;
+ proposalId?: string;
+ transactionId?: string;
}
```
@@ -1520,6 +1674,40 @@ export interface WireInfo {
```
```typescript
+export type WireFeeMap = {
+ [wireMethod: string]: WireFee[];
+};
+
+```
+```typescript
+/**
+ * Wire fee for one wire method
+ */
+export interface WireFee {
+ /**
+ * Fee for wire transfers.
+ */
+ wireFee: AmountString;
+ /**
+ * Fees to close and refund a reserve.
+ */
+ closingFee: AmountString;
+ /**
+ * Start date of the fee.
+ */
+ startStamp: TalerProtocolTimestamp;
+ /**
+ * End date of the fee.
+ */
+ endStamp: TalerProtocolTimestamp;
+ /**
+ * Signature made by the exchange master key.
+ */
+ sig: string;
+}
+
+```
+```typescript
export interface ExchangeWireAccount {
payto_uri: string;
conversion_url?: string;
@@ -1532,6 +1720,16 @@ export interface ExchangeWireAccount {
```
```typescript
+export type DenomOperationMap<T> = {
+ [op in DenomOperation]: T;
+};
+
+```
+```typescript
+export type DenomOperation = "deposit" | "withdraw" | "refresh" | "refund";
+
+```
+```typescript
export interface FeeDescription {
group: string;
from: AbsoluteTime;
@@ -2374,6 +2572,29 @@ export interface ValidateIbanResponse {
```
+### CanonicalizeBaseUrlOp
+```typescript
+export type CanonicalizeBaseUrlOp = {
+ op: WalletApiOperation.CanonicalizeBaseUrl;
+ request: CanonicalizeBaseUrlRequest;
+ response: CanonicalizeBaseUrlResponse;
+};
+// CanonicalizeBaseUrl = "canonicalizeBaseUrl"
+
+```
+```typescript
+export interface CanonicalizeBaseUrlRequest {
+ url: string;
+}
+
+```
+```typescript
+export interface CanonicalizeBaseUrlResponse {
+ url: string;
+}
+
+```
+
### ExportDbOp
```typescript
/**
@@ -2596,11 +2817,28 @@ export type GetPendingTasksOp = {
export type GetActiveTasksOp = {
op: WalletApiOperation.GetActiveTasks;
request: EmptyObject;
- response: GetActiveTasks;
+ response: GetActiveTasksResponse;
};
// GetActiveTasks = "getActiveTasks"
```
+```typescript
+export interface GetActiveTasksResponse {
+ tasks: ActiveTask[];
+}
+
+```
+```typescript
+export interface ActiveTask {
+ taskId: string;
+ transaction: TransactionIdStr | undefined;
+ firstTry: AbsoluteTime | undefined;
+ nextTry: AbsoluteTime | undefined;
+ retryCounter: number | undefined;
+ lastError: TalerErrorDetail | undefined;
+}
+
+```
### DumpCoinsOp
```typescript
@@ -2819,6 +3057,20 @@ export type TestingWaitTransactionsFinalOp = {
```
+### TestingWaitTasksDoneOp
+```typescript
+/**
+ * Wait until all transactions are in a final state.
+ */
+export type TestingWaitTasksDoneOp = {
+ op: WalletApiOperation.TestingWaitTasksDone;
+ request: EmptyObject;
+ response: EmptyObject;
+};
+// TestingWaitTasksDone = "testingWaitTasksDone"
+
+```
+
### TestingWaitRefreshesFinalOp
```typescript
/**
@@ -2865,6 +3117,17 @@ export type TestingPingOp = {
```
+### TestingGetReserveHistoryOp
+```typescript
+export type TestingGetReserveHistoryOp = {
+ op: WalletApiOperation.TestingGetReserveHistory;
+ request: EmptyObject;
+ response: any;
+};
+// TestingGetReserveHistory = "testingGetReserveHistory"
+
+```
+
### TestingGetDenomStatsOp
```typescript
/**
@@ -2954,6 +3217,7 @@ export interface PartialWalletRunConfig {
builtin?: Partial<WalletRunConfig["builtin"]>;
testing?: Partial<WalletRunConfig["testing"]>;
features?: Partial<WalletRunConfig["features"]>;
+ lazyTaskLoop?: Partial<WalletRunConfig["lazyTaskLoop"]>;
}
```
```typescript
@@ -2987,6 +3251,15 @@ export interface WalletRunConfig {
features: {
allowHttp: boolean;
};
+ /**
+ * Start processing tasks only when explicitly required, even after
+ * init has been called.
+ *
+ * Useful when the wallet is started to make single read-only request,
+ * as otherwise wallet-core starts making network request and process
+ * unrelated pending tasks.
+ */
+ lazyTaskLoop: boolean;
}
```
```typescript
@@ -3029,14 +3302,40 @@ export interface WalletCoreVersion {
}
```
```typescript
+// group: Initialization
+type EmptyObject = Record<string, never>;
+```
+```typescript
export type ScopeInfo = ScopeInfoGlobal | ScopeInfoExchange | ScopeInfoAuditor;
```
```typescript
-export type AmountString = string & {
- [__amount_str]: true;
+export type ScopeInfoGlobal = {
+ type: ScopeType.Global;
+ currency: string;
+};
+```
+```typescript
+export type ScopeInfoExchange = {
+ type: ScopeType.Exchange;
+ currency: string;
+ url: string;
};
```
```typescript
+export type ScopeInfoAuditor = {
+ type: ScopeType.Auditor;
+ currency: string;
+ url: string;
+};
+```
+```typescript
+export type AmountString =
+ | (string & {
+ [__amount_str]: true;
+ })
+ | LitAmountString;
+```
+```typescript
/**
* How the amount should be interpreted in a transaction
* Effective = how the balance is change
@@ -3076,7 +3375,6 @@ export type Transaction =
| TransactionWithdrawal
| TransactionPayment
| TransactionRefund
- | TransactionReward
| TransactionRefresh
| TransactionDeposit
| TransactionPeerPullCredit
@@ -3149,7 +3447,6 @@ export declare enum TransactionType {
Payment = "payment",
Refund = "refund",
Refresh = "refresh",
- Reward = "reward",
Deposit = "deposit",
PeerPushDebit = "peer-push-debit",
PeerPushCredit = "peer-push-credit",
@@ -3243,6 +3540,7 @@ export declare enum TransactionMinorState {
RefundAvailable = "refund-available",
AcceptRefund = "accept-refund",
PaidByOther = "paid-by-other",
+ CompletedByOtherWallet = "completed-by-other-wallet",
}
```
```typescript
@@ -3551,17 +3849,6 @@ export interface RefundPaymentInfo {
}
```
```typescript
-export interface TransactionReward extends TransactionCommon {
- type: TransactionType.Reward;
- amountRaw: AmountString;
- /**
- * More information about the merchant
- */
- amountEffective: AmountString;
- merchantBaseUrl: string;
-}
-```
-```typescript
/**
* A transaction shown for refreshes.
* Only shown for (1) refreshes not associated with other transactions
@@ -3808,6 +4095,23 @@ export interface AbortTransactionRequest {
}
```
```typescript
+export interface WithdrawUriInfoResponse {
+ operationId: string;
+ status: WithdrawalOperationStatus;
+ confirmTransferUrl?: string;
+ amount: AmountString;
+ defaultExchangeBaseUrl?: string;
+ possibleExchanges: ExchangeListItem[];
+}
+```
+```typescript
+export type WithdrawalOperationStatus =
+ | "pending"
+ | "selected"
+ | "aborted"
+ | "confirmed";
+```
+```typescript
/**
* Info about an exchange entry in the wallet.
*/
@@ -4066,7 +4370,6 @@ export interface AddExchangeRequest {
* @deprecated use a separate API call to start a forced exchange update instead
*/
forceUpdate?: boolean;
- masterPub?: string;
}
```
```typescript