commit a30f615c1821e6c05d24f3cea116515f800896cd
parent 1c2178bfb97585e7550388f616e57d005fd6fa47
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Thu, 8 May 2025 10:45:27 +0200
Merge branch 'master' of git+ssh://git.taler.net/taler-docs
Diffstat:
19 files changed, 850 insertions(+), 1121 deletions(-)
diff --git a/checklists/checklist-demo-upgrade.rst b/checklists/checklist-demo-upgrade.rst
@@ -1,5 +1,7 @@
GNU Taler Demo Upgrade Checklist
---------------------------------
+================================
+
+.. toctree::
.. |democheck| raw:: html
@@ -7,24 +9,24 @@ GNU Taler Demo Upgrade Checklist
Domains
-^^^^^^^
+-------
The checklist uses the ``demo.taler.net`` domains. However,
the same sandcastle demo can also be hosted at other domains.
The same instructions should apply.
Post-upgrade checks
-^^^^^^^^^^^^^^^^^^^
+-------------------
- |democheck| Run the headless wallet to check that services are actually working:
.. code-block:: console
- taler-wallet-cli api 'runIntegrationTestV2' '{"exchangeBaseUrl":"https://exchange.demo.taler.net", "corebankApiBaseUrl": "https://bank.demo.taler.net", "merchantBaseUrl": "https://backend.demo.taler.net", "merchantAuthToken":"secret-token:sandbox"}'
+ taler-wallet-cli api 'runIntegrationTestV2' '{"exchangeBaseUrl":"https://exchange.demo.taler.net/", "corebankApiBaseUrl": "https://bank.demo.taler.net/", "merchantBaseUrl": "https://backend.demo.taler.net/", "merchantAuthToken":"secret-token:sandbox"}'
Basics
-^^^^^^
+-------
- |democheck| Visit https://demo.taler.net/ to see if the landing page is displayed correctly
- |democheck| landing language switcher
@@ -32,19 +34,8 @@ Basics
- |democheck| see if the wallet presence indicator is updated correctly (in browsers).
- |democheck| Visit https://exchange.demo.taler.net/terms to check ToS works
-Wallets
-^^^^^^^
-
-We consider the following published wallets to be "production wallets":
-
-* Browser: Firefox Add-On Store
-* Browser: Chrome Web Store
-* Android: Google Play / F-Droid / APK
-* iOS: Apple Store / Testflight
-
-
-libeufin - Withdraw & Deposit
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LibEuFIn
+--------
To run those test you need one wallet.
@@ -54,41 +45,6 @@ To run those test you need one wallet.
- |democheck| bank logout
- |democheck| bank login
-- |democheck| wallet withdrawal payto-URI
-
- - trigger withdraw from wallet & copy URI
- - paste URI in bank & pay more
- - wallet redirect to success and receive more
- - paste URI in bank & pay -> bounce
-
-- |democheck| bank withdrawal success
-
- - bank trigger withdrawal & save QR code
- - wallet scan QR code
- - bank pay
- - wallet redirect to success
- - wallet scan QR code -> redirect to success
-
-- |democheck| bank withdrawal failure
-
- - bank trigger withdrawal
- - wallet scan QR code
- - bank cancel
- - wallet redirect to failure
- - wallet scan QR code -> redirect to failure
-
-- |democheck| deposit existing account
-
- - check bank1 account is already registered
- - trigger deposit using bank1
- - check deposit in bank1 account
-
-- |democheck| deposit new account
-
- - add bank2 account
- - trigger deposit using bank2
- - check deposit in bank2 account
-
- |democheck| account management
- add bank2 again -> create duplicate (TODO redirection a warning https://bugs.gnunet.org/view.php?id=9698)
@@ -105,123 +61,11 @@ To run those test you need one wallet.
- |democheck| (MB-only) manually import transactions from bank account
- |democheck| (MB-only) manually export transactions to bank account
-P2P payments
-^^^^^^^^^^^^
-
-To run those test you need three wallets, you should ideally use three different platforms.
-
-TODO: some pay* interactions should become unnecessary in a future update: https://bugs.gnunet.org/view.php?id=9670
-TODO: check different kind of failure (aborted, expired, already complete by another wallet) are correctly shown
-
-Concurrent flow
-""""""""""""""""
-
-Test what happens when two wallets compete on the same payment.
-
-- initiate payment in wallet1 and save URI
-- scan QR code in wallet2 & wallet3
-- pay with wallet2 -> redirect to success
-- check wallet1 redirect to success
-- pay* with wallet3 -> redirect to failure
-- paste URI in wallet2 -> pay* -> redirect to success
-- paste URI in wallet3 -> pay* -> redirect to failure
-
-
-Abort flow
-""""""""""
-
-Test what happens when a payment is aborted.
-- initiate payment in wallet1 and save URI
-- scan QR code in wallet2
-- abort in wallet1 -> redirect to failure
-- pay* with wallet2 -> redirect to failure
-- paste URI in wallet3 -> pay* -> redirect to failure
-
-Expire flow
-"""""""""""
-
-Test what happens when a payment is expired.
-
-- initiate payment in wallet1 with short expiration and save URI
-- scan QR code in wallet2
-- wait for expiration
-- check wallet1 redirect to failure
-- pay* with wallet2 -> redirect to failure
-- paste URI in wallet3 -> pay* -> redirect to failure
-
-Network flow
-""""""""""""
-
-Test what happens when the wallet does not have internet access.
-
-- disable network in wallet1
-- initiate payment in wallet1
-- check that a loading state with warning is shown
-- enable network in wallet1
-- check that wallet1 recover and display QR code and URI
-- disable network in wallet2
-- scan QR code -> network failure screen
-- enable network in wallet2
-- pay with wallet2 -> redirect to success
-
-Checklist
-"""""""""
-
-- |democheck| pull payment (receive) concurrent flow
-- |democheck| pull payment (receive) abort flow
-- |democheck| pull payment (receive) expire flow
-- |democheck| pull payment (receive) network flow
-
-- |democheck| push payment (send) concurrent flow
-- |democheck| push payment (send) abort flow
-- |democheck| push payment (send) expire flow
-- |democheck| push payment (send) network flow
-
-- |democheck| check pull payment insufficient fund error on completion
-- |democheck| check push payment insufficient fund error on initiation
-
-- |democheck| sending money back from wallet to bank account
-- |democheck| wallet transaction history rendering
-- |democheck| delete history entry
-
-
-Exchange management
-"""""""""""""""""""
-
-- |democheck| Try to explicitly reload exchange keys (still needed?)
-- |democheck| Have wallet show ToS of an exchange
-- |democheck| Have wallet show PP of an exchange
-- |democheck| Remove exchange with remaining balance
-- |democheck| Check remaining balance is deposited into origin account
-
-
-Android Cashier App
-^^^^^^^^^^^^^^^^^^^
-
-To run those test you need one wallet and a bank account.
-
-- |democheck| Configure cashier app with libeufin account
-- |democheck| Lock and reconnect
-- |democheck| Reconfigure
-- |democheck| Check insufficient balance error
-- |democheck| Check zero error
-
-- |democheck| Withdraw cash from cashier
-
- - Initiate withdraw in cashier
- - Scan QR code in wallet & accept
- - Pay in cashier
-
-- |democheck| Withdraw cash from cashier & bank
-
- - Initiate withdraw in cashier
- - Scan QR code in wallet & accept
- - Pay from bank account website (TODO do we want this to be proposed ?)
- - Check cashier redirect
+.. include:: frags/checklist-wallet.rst
Blog demo
-^^^^^^^^^
+---------
- |democheck| Visit https://shop.demo.taler.net/
- |democheck| blog page article list renders
@@ -252,7 +96,7 @@ Blog demo
Donation demo
-^^^^^^^^^^^^^
+-------------
- |democheck| Reset wallet
- |democheck| Withdraw age-restricted coins (< 14)
@@ -263,171 +107,4 @@ Donation demo
that the payment is requested again, instead of showing the previous
fulfillment page.
-
-Merchant SPA
-^^^^^^^^^^^^
-
-- |democheck| test SPA loads
-- |democheck| check SPA language switcher
-- |democheck| try to login with wrong password
-- |democheck| try to login with correct password
-- |democheck| create instance, check default is set to cover (STEFAN) fees
-- |democheck| modify instance
-- |democheck| add bank account
-- |democheck| (if KYC is on) check KYC AUTH request notification is requested
-- |democheck| edit bank account
-- |democheck| (if KYC is on) check KYC AUTH request notification is requested
-- |democheck| (if KYC is on) perform KYC AUTH wire transfer
-- |democheck| (if KYC is on) check KYC AUTH request notification is cleared
-- |democheck| remove bank account
-- |democheck| check order creation fails without bank account
-- |democheck| add bank account again
-- |democheck| (if KYC is on) check KYC AUTH request notification remains off
-- |democheck| add inventory category
-- |democheck| add 2nd inventory category
-- |democheck| edit inventory category
-- |democheck| add product with 1 in stock and preview image and two categories
-- |democheck| edit inventory product
-- |democheck| add 2nd inventory product
-- |democheck| delete 2nd inventory product
-- |democheck| add "advanced" order with inventory product and a 2 minute wire delay
-- |democheck| claim order, check available stock goes down in inventory
-- |democheck| create 2nd order, check this fails due to missing inventory
-- |democheck| pay for 1st order with wallet
-- |democheck| check transaction history for preview image
-- |democheck| trigger partial refund
-- |democheck| accept refund with wallet
-- |democheck| create template with fixed summary, default editable price
-- |democheck| scan template QR code, edit price and pay
-- |democheck| add TOTP device (using some TOTP app to share secret with)
-- |democheck| edit TOTP device (using some TOTP app to share secret with)
-- |democheck| edit template to add TOTP device, set price to fixed, summary to be entered
-- |democheck| scan template QR code, edit summary and pay
-- |democheck| check displayed TOTP code matches TOTP app
-- |democheck| delete TOTP device
-- |democheck| delete template device
-- |democheck| do manual wire transfer in bank to establish reserve funding
-- |democheck| check that partially refunded order is marked as awaiting wire transfer
-- |democheck| check bank wired funds to merchant (if needed, wait)
-- |democheck| add bank wire transfer manually to backend
-- |democheck| change settings for merchant to not pay for (STEFAN) fees
-- |democheck| create and pay for another order with 1 minute wire transfer delay
-- |democheck| edit bank account details, adding revenue facade with credentials
-- |democheck| wait and check if wire transfer is automatically imported
-- |democheck| check that orders are marked as completed
-
-
-Android Merchant PoS
-^^^^^^^^^^^^^^^^^^^^
-
-* |democheck| Configure using instance with configured inventory
-* |democheck| Check categories and products show (with images!)
-* |democheck| Add product to order
-* |democheck| Add product again to order (+)
-* |democheck| Remove product from order (-)
-* |democheck| Request payment
-* |democheck| Abort payment, check order can still be edited
-* |democheck| Request and make payment, check payment confirmed
-* |democheck| Create another order, delete/abort it without paying
-
-Auditor
-^^^^^^^
-
-- |democheck| Check auditor SPA is access controlled
-- |democheck| Check /config endpoint (and implied POST /deposit-confirmation are public)
-- |democheck| Check exchange /keys reports auditor's existence
-- |democheck| Check auditor imports exchange transaction data (non-zero progress points)
-- |democheck| Check auditor SPA reports no failures from previous transactions
-- |democheck| Check auditor SPA bank balance matches exchange bank balance
-
-
-Exchange KYC Triggers
-^^^^^^^^^^^^^^^^^^^^^
-
-Each of these checks should be done with a fresh account, merchant instance
-or wallet (if they previously ran into a KYC check already). Specific amounts
-depend on the configured trigger thresholds.
-
-- |democheck| withdraw: withdraw large amount, make sure it is forbidden or runs into KYC check (shown by wallet)
-- |democheck| aggregation: pay large order, make sure it runs into aggregate KYC check (shown by merchant SPA)
-- |democheck| deposit large amount into other account with wallet, make sure it runs into KYC AUTH + KYC check (shown by wallet)
-- |democheck| balance: withdraw large amounts from multiple accounts, make sure it is forbidden or runs into KYC check (shown by wallet)
-- |democheck| P2P receive large amount: make sure it runs into KYC check (shown by wallet)
-- |democheck| P2P invoice large amount: make sure it runs into KYC check (shown by wallet)
-- |democheck| Onboarding check (KYC AUTH, ToS-acceptance) triggered for new merchant accounts
-
-
-Exchange KYC SPA
-^^^^^^^^^^^^^^^^
-
-Consult the specific deployment's KYC configuration to see which KYC processes
-are used.
-
-- |democheck| check SPA language switcher
-- |democheck| check INFO page(s) where KYC status is shown
-- |democheck| check LINK page(s) with link to external KYC process (e.g. challenger)
-- |democheck| (if possible) check challenger SPA language switcher
-- |democheck| (if possible) check KYC SPA main page with multiple choices (AND/OR combinators)
-- |democheck| perform LINKed external process, check data imported correctly
-- |democheck| check FORM pages for each possible KYC form of the deployment
-- |democheck| submit FORM pages with valid but also obviously invalid data (if applicable)
-- |democheck| check main page updated to next stage correctly after each possible FORM
-- |democheck| check SMS generation (and restriction to CH-only) by SMS challenger (telesign!), production-only (not for demo)
-- |democheck| check Postal mail generation (incl. address conversion to proper format) by Postal challenger (pingen!), production-only (not for demo)
-
-
-Exchange AML SPA
-^^^^^^^^^^^^^^^^
-
-- |democheck| check SPA language switcher
-- |democheck| load, enable account using taler-exchange-offline
-- |democheck| log out
-- |democheck| check log in fails from different browser with same password
-- |democheck| check log in fails from original browser with incorrect password
-- |democheck| check log in succeeds with correct password
-- |democheck| enter data in each available AML form
-- |democheck| check data of AML form shows properly in account history
-- |democheck| submit AML form and trigger event (explicitly or by setting account property)
-- |democheck| check event statistics are properly updated and shown on main page
-- |democheck| submit AML form and change account thresholds for some operation with VERBOTEN
-- |democheck| check new threshold is now enforced by the exchange (VERBOTEN)
-- |democheck| submit AML form and change account threshold for some operation to trigger KYC check
-- |democheck| check new threshold is now enforced by exchange and KYC check is triggered
-- |democheck| submit AML form and change account threshold for some operation to trigger AML investigation (and clear investigation flag)
-- |democheck| check new threshold marks account again for investigation after threshold is crossed
-- |democheck| submit AML form with a short expiration (minutes) and a fallback of "investigate again"
-- |democheck| check new rules are applied until expiration
-- |democheck| check account is automatically listed again for investigation after expiration time is reached
-- |democheck| view historic AML decisions in history, view submitted KYC data
-
-
-Sanction lists
-^^^^^^^^^^^^^^
-
-- |democheck| ensure account with KYC data exists in the system
-- |democheck| manually write santion list with user that clearly does not match
-- |democheck| import sanction list, check nothing is done
-- |democheck| edit sanction list to match the existing account a bit
-- |democheck| import sanction list, check account is flagged for investigation by AML staff but remains operational
-- |democheck| clear the investigation flag
-- |democheck| edit sanction list to match the existing account perfectly
-- |democheck| import sanction list, check account is flagged for investigation by AML staff and also frozen (all limits 0, not exposed)
-- |democheck| manually clear user and unfreeze account in AML SPA (setting "SANCTION-OVERRIDE: $DATE" property)
-- |democheck| re-import sanction list with yet another user and cleared user
-- |democheck| check manually cleared user is not re-frozen (due to "SANCTION-OVERRIDE" property with date in the future)
-- |democheck| add user matching new entry in sanction list
-- |democheck| check new user is auto-frozen and flagged for investigation
-
-
-Shutdown
-^^^^^^^^
-
-- |democheck| create two full wallets, fill one only via (a large) P2P transfer
-- |democheck| revoke highest-value denomination
-- |democheck| spend money in a wallet such that the balance falls below highest denomination value
-- |democheck| revoke all remaining denominations
-- |democheck| fail to spend any more money
-- |democheck| if wallet was filled via p2p payments, wallet asks for target deposit account (exchange going out of business)
-- |democheck| enter bank account (if possible)
-- |democheck| wallet balance goes to zero
-- |democheck| specified bank account receives remaining balance
+.. include:: frags/checklist-common.rst
+\ No newline at end of file
diff --git a/checklists/checklist-gls.rst b/checklists/checklist-gls.rst
@@ -0,0 +1,8 @@
+GLS GNU Taler Integration Checklist
+===================================
+
+.. toctree::
+
+.. include:: frags/checklist-wallet.rst
+
+.. include:: frags/checklist-common.rst
+\ No newline at end of file
diff --git a/checklists/frags/checklist-common.rst b/checklists/frags/checklist-common.rst
@@ -0,0 +1,167 @@
+Merchant SPA
+------------
+
+- |democheck| test SPA loads
+- |democheck| check SPA language switcher
+- |democheck| try to login with wrong password
+- |democheck| try to login with correct password
+- |democheck| create instance, check default is set to cover (STEFAN) fees
+- |democheck| modify instance
+- |democheck| add bank account
+- |democheck| (if KYC is on) check KYC AUTH request notification is requested
+- |democheck| edit bank account
+- |democheck| (if KYC is on) check KYC AUTH request notification is requested
+- |democheck| (if KYC is on) perform KYC AUTH wire transfer
+- |democheck| (if KYC is on) check KYC AUTH request notification is cleared
+- |democheck| remove bank account
+- |democheck| check order creation fails without bank account
+- |democheck| add bank account again
+- |democheck| (if KYC is on) check KYC AUTH request notification remains off
+- |democheck| add inventory category
+- |democheck| add 2nd inventory category
+- |democheck| edit inventory category
+- |democheck| add product with 1 in stock and preview image and two categories
+- |democheck| edit inventory product
+- |democheck| add 2nd inventory product
+- |democheck| delete 2nd inventory product
+- |democheck| add "advanced" order with inventory product and a 2 minute wire delay
+- |democheck| claim order, check available stock goes down in inventory
+- |democheck| create 2nd order, check this fails due to missing inventory
+- |democheck| pay for 1st order with wallet
+- |democheck| check transaction history for preview image
+- |democheck| trigger partial refund
+- |democheck| accept refund with wallet
+- |democheck| create template with fixed summary, default editable price
+- |democheck| scan template QR code, edit price and pay
+- |democheck| add TOTP device (using some TOTP app to share secret with)
+- |democheck| edit TOTP device (using some TOTP app to share secret with)
+- |democheck| edit template to add TOTP device, set price to fixed, summary to be entered
+- |democheck| scan template QR code, edit summary and pay
+- |democheck| check displayed TOTP code matches TOTP app
+- |democheck| delete TOTP device
+- |democheck| delete template device
+- |democheck| do manual wire transfer in bank to establish reserve funding
+- |democheck| check that partially refunded order is marked as awaiting wire transfer
+- |democheck| check bank wired funds to merchant (if needed, wait)
+- |democheck| add bank wire transfer manually to backend
+- |democheck| change settings for merchant to not pay for (STEFAN) fees
+- |democheck| create and pay for another order with 1 minute wire transfer delay
+- |democheck| edit bank account details, adding revenue facade with credentials
+- |democheck| wait and check if wire transfer is automatically imported
+- |democheck| check that orders are marked as completed
+
+
+Android Merchant PoS
+--------------------
+
+* |democheck| Configure using instance with configured inventory
+* |democheck| Check categories and products show (with images!)
+* |democheck| Add product to order
+* |democheck| Add product again to order (+)
+* |democheck| Remove product from order (-)
+* |democheck| Request payment
+* |democheck| Abort payment, check order can still be edited
+* |democheck| Request and make payment, check payment confirmed
+* |democheck| Create another order, delete/abort it without paying
+
+Auditor
+-------
+
+- |democheck| Check auditor SPA is access controlled
+- |democheck| Check /config endpoint (and implied POST /deposit-confirmation are public)
+- |democheck| Check exchange /keys reports auditor's existence
+- |democheck| Check auditor imports exchange transaction data (non-zero progress points)
+- |democheck| Check auditor SPA reports no failures from previous transactions
+- |democheck| Check auditor SPA bank balance matches exchange bank balance
+
+
+Exchange KYC Triggers
+---------------------
+
+Each of these checks should be done with a fresh account, merchant instance
+or wallet (if they previously ran into a KYC check already). Specific amounts
+depend on the configured trigger thresholds.
+
+- |democheck| withdraw: withdraw large amount, make sure it is forbidden or runs into KYC check (shown by wallet)
+- |democheck| aggregation: pay large order, make sure it runs into aggregate KYC check (shown by merchant SPA)
+- |democheck| deposit large amount into other account with wallet, make sure it runs into KYC AUTH + KYC check (shown by wallet)
+- |democheck| balance: withdraw large amounts from multiple accounts, make sure it is forbidden or runs into KYC check (shown by wallet)
+- |democheck| P2P receive large amount: make sure it runs into KYC check (shown by wallet)
+- |democheck| P2P invoice large amount: make sure it runs into KYC check (shown by wallet)
+- |democheck| Onboarding check (KYC AUTH, ToS-acceptance) triggered for new merchant accounts
+
+
+Exchange KYC SPA
+----------------
+
+Consult the specific deployment's KYC configuration to see which KYC processes
+are used.
+
+- |democheck| check SPA language switcher
+- |democheck| check INFO page(s) where KYC status is shown
+- |democheck| check LINK page(s) with link to external KYC process (e.g. challenger)
+- |democheck| (if possible) check challenger SPA language switcher
+- |democheck| (if possible) check KYC SPA main page with multiple choices (AND/OR combinators)
+- |democheck| perform LINKed external process, check data imported correctly
+- |democheck| check FORM pages for each possible KYC form of the deployment
+- |democheck| submit FORM pages with valid but also obviously invalid data (if applicable)
+- |democheck| check main page updated to next stage correctly after each possible FORM
+- |democheck| check SMS generation (and restriction to CH-only) by SMS challenger (telesign!), production-only (not for demo)
+- |democheck| check Postal mail generation (incl. address conversion to proper format) by Postal challenger (pingen!), production-only (not for demo)
+
+
+Exchange AML SPA
+----------------
+
+- |democheck| check SPA language switcher
+- |democheck| load, enable account using taler-exchange-offline
+- |democheck| log out
+- |democheck| check log in fails from different browser with same password
+- |democheck| check log in fails from original browser with incorrect password
+- |democheck| check log in succeeds with correct password
+- |democheck| enter data in each available AML form
+- |democheck| check data of AML form shows properly in account history
+- |democheck| submit AML form and trigger event (explicitly or by setting account property)
+- |democheck| check event statistics are properly updated and shown on main page
+- |democheck| submit AML form and change account thresholds for some operation with VERBOTEN
+- |democheck| check new threshold is now enforced by the exchange (VERBOTEN)
+- |democheck| submit AML form and change account threshold for some operation to trigger KYC check
+- |democheck| check new threshold is now enforced by exchange and KYC check is triggered
+- |democheck| submit AML form and change account threshold for some operation to trigger AML investigation (and clear investigation flag)
+- |democheck| check new threshold marks account again for investigation after threshold is crossed
+- |democheck| submit AML form with a short expiration (minutes) and a fallback of "investigate again"
+- |democheck| check new rules are applied until expiration
+- |democheck| check account is automatically listed again for investigation after expiration time is reached
+- |democheck| view historic AML decisions in history, view submitted KYC data
+
+
+Sanction lists
+--------------
+
+- |democheck| ensure account with KYC data exists in the system
+- |democheck| manually write santion list with user that clearly does not match
+- |democheck| import sanction list, check nothing is done
+- |democheck| edit sanction list to match the existing account a bit
+- |democheck| import sanction list, check account is flagged for investigation by AML staff but remains operational
+- |democheck| clear the investigation flag
+- |democheck| edit sanction list to match the existing account perfectly
+- |democheck| import sanction list, check account is flagged for investigation by AML staff and also frozen (all limits 0, not exposed)
+- |democheck| manually clear user and unfreeze account in AML SPA (setting "SANCTION-OVERRIDE: $DATE" property)
+- |democheck| re-import sanction list with yet another user and cleared user
+- |democheck| check manually cleared user is not re-frozen (due to "SANCTION-OVERRIDE" property with date in the future)
+- |democheck| add user matching new entry in sanction list
+- |democheck| check new user is auto-frozen and flagged for investigation
+
+
+Shutdown
+--------
+
+- |democheck| create two full wallets, fill one only via (a large) P2P transfer
+- |democheck| revoke highest-value denomination
+- |democheck| spend money in a wallet such that the balance falls below highest denomination value
+- |democheck| revoke all remaining denominations
+- |democheck| fail to spend any more money
+- |democheck| if wallet was filled via p2p payments, wallet asks for target deposit account (exchange going out of business)
+- |democheck| enter bank account (if possible)
+- |democheck| wallet balance goes to zero
+- |democheck| specified bank account receives remaining balance
diff --git a/checklists/frags/checklist-wallet.rst b/checklists/frags/checklist-wallet.rst
@@ -0,0 +1,171 @@
+
+.. |democheck| raw:: html
+
+ <input type="checkbox">
+
+Wallets
+-------
+
+We consider the following published wallets to be "production wallets":
+
+* Browser: Firefox Add-On Store
+* Browser: Chrome Web Store
+* Android: Google Play / F-Droid / APK
+* iOS: Apple Store / Testflight
+
+To run those tests you need one to three wallets (you should ideally use three different platforms) and one to two opened bank accounts into a bank with Taler Integration.
+
+Withdraw & Deposit
+^^^^^^^^^^^^^^^^^^
+
+To run those tests you need one wallet and two bank accounts.
+
+- |democheck| wallet withdrawal payto-URI
+
+ - trigger withdraw from wallet & copy URI
+ - paste URI in bank & pay more
+ - wallet redirect to success and receive more
+ - paste URI in bank & pay -> bounce
+
+- |democheck| bank withdrawal success
+
+ - bank trigger withdrawal & save QR code
+ - wallet scan QR code
+ - bank pay
+ - wallet redirect to success
+ - wallet scan QR code -> redirect to success
+
+- |democheck| bank withdrawal failure
+
+ - bank trigger withdrawal
+ - wallet scan QR code
+ - bank cancel
+ - wallet redirect to failure
+ - wallet scan QR code -> redirect to failure
+
+- |democheck| deposit existing account
+
+ - check bank1 account is already registered
+ - trigger deposit using bank1
+ - check deposit in bank1 account
+
+- |democheck| deposit new account
+
+ - add bank2 account
+ - trigger deposit using bank2
+ - check deposit in bank2 account
+
+P2P payments
+^^^^^^^^^^^^
+
+TODO: some pay* interactions should become unnecessary in a future update: https://bugs.gnunet.org/view.php?id=9670
+TODO: check different kind of failure (aborted, expired, already complete by another wallet) are correctly shown
+
+To run those test you need three wallets.
+
+Concurrent flow
+""""""""""""""""
+
+Test what happens when two wallets compete on the same payment.
+
+- initiate payment in wallet1 and save URI
+- scan QR code in wallet2 & wallet3
+- pay with wallet2 -> redirect to success
+- check wallet1 redirect to success
+- pay* with wallet3 -> redirect to failure
+- paste URI in wallet2 -> pay* -> redirect to success
+- paste URI in wallet3 -> pay* -> redirect to failure
+
+
+Abort flow
+""""""""""
+
+Test what happens when a payment is aborted.
+
+- initiate payment in wallet1 and save URI
+- scan QR code in wallet2
+- abort in wallet1 -> redirect to failure
+- pay* with wallet2 -> redirect to failure
+- paste URI in wallet3 -> pay* -> redirect to failure
+
+Expire flow
+"""""""""""
+
+Test what happens when a payment is expired.
+
+- initiate payment in wallet1 with short expiration and save URI
+- scan QR code in wallet2
+- wait for expiration
+- check wallet1 redirect to failure
+- pay* with wallet2 -> redirect to failure
+- paste URI in wallet3 -> pay* -> redirect to failure
+
+Network flow
+""""""""""""
+
+Test what happens when the wallet does not have internet access.
+
+- disable network in wallet1
+- initiate payment in wallet1
+- check that a loading state with warning is shown
+- enable network in wallet1
+- check that wallet1 recover and display QR code and URI
+- disable network in wallet2
+- scan QR code -> network failure screen
+- enable network in wallet2
+- pay with wallet2 -> redirect to success
+
+Checklist
+"""""""""
+
+- |democheck| pull payment (receive) concurrent flow
+- |democheck| pull payment (receive) abort flow
+- |democheck| pull payment (receive) expire flow
+- |democheck| pull payment (receive) network flow
+
+- |democheck| push payment (send) concurrent flow
+- |democheck| push payment (send) abort flow
+- |democheck| push payment (send) expire flow
+- |democheck| push payment (send) network flow
+
+- |democheck| check pull payment insufficient fund error on completion
+- |democheck| check push payment insufficient fund error on initiation
+
+- |democheck| sending money back from wallet to bank account
+- |democheck| wallet transaction history rendering
+- |democheck| delete history entry
+
+
+Exchange management
+"""""""""""""""""""
+
+- |democheck| Try to explicitly reload exchange keys (still needed?)
+- |democheck| Have wallet show ToS of an exchange
+- |democheck| Have wallet show PP of an exchange
+- |democheck| Remove exchange with remaining balance
+- |democheck| Check remaining balance is deposited into origin account
+
+
+Android Cashier App
+^^^^^^^^^^^^^^^^^^^
+
+To run those test you need one wallet and a bank account.
+
+- |democheck| Configure cashier app with libeufin account
+- |democheck| Lock and reconnect
+- |democheck| Reconfigure
+- |democheck| Check insufficient balance error
+- |democheck| Check zero error
+
+- |democheck| Withdraw cash from cashier
+
+ - Initiate withdraw in cashier
+ - Scan QR code in wallet & accept
+ - Pay in cashier
+
+- |democheck| Withdraw cash from cashier & bank
+
+ - Initiate withdraw in cashier
+ - Scan QR code in wallet & accept
+ - Pay from bank account website (TODO do we want this to be proposed ?)
+ - Check cashier redirect
diff --git a/conf.py b/conf.py
@@ -82,7 +82,7 @@ master_doc = "index"
# General information about the project.
project = "GNU Taler"
-copyright = "2014-2024 Taler Systems SA (GPLv3+ or GFDL 1.3+)"
+copyright = "2014-2025 Taler Systems SA (GPLv3+ or GFDL 1.3+)"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/core/api-challenger.rst b/core/api-challenger.rst
@@ -181,10 +181,15 @@ Setup
.. ts:def:: ChallengeSetupRequest
- interface ChallengeSetupResponse {
+ interface ChallengeSetupRequest {
// If true, the given address should not be edited.
// Defaults to 'false' if not specified.
- read_only: boolean;
+ read_only?: boolean;
+
+ // Optional, additional fields to pre-populate
+ // the address to be validated.
+ // The fields depend on the challenger type.
+ [x: string]: any;
}
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
@@ -60,7 +60,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 **v25**.
+ This specification corresponds to ``current`` protocol being **v27**.
**Response:**
@@ -1483,7 +1483,6 @@ Bachelor thesis of Gian Demarmels and Lucien Heuzeveldt,
for details.
-.. note:: This endpoint is available since v26 of the API and is a substitute
for ``/csr-withdraw`` and ``/csr-melt``.
@@ -1650,10 +1649,9 @@ exchange.
.. _withdraw:
.. http:post:: /withdraw
- .. note::
- This endpoint is available starting with API version v26.
- It combines and replaces the endpoints ``/reserves/$RESERVE_PUB/batch-withdraw``
- and ``/reserves/$RESERVE_PUB/age-withdraw``.
+ .. note:: This endpoint is available starting with API version **v26**.
+ It combines and replaces the endpoints ``/reserves/$RESERVE_PUB/batch-withdraw``
+ and ``/reserves/$RESERVE_PUB/age-withdraw``.
Withdraw multiple coins from the same reserve. Note that the client should
commit all of the request details, including the private key of the coins and
@@ -1907,193 +1905,10 @@ exchange.
}
-.. http:post:: /csr-withdraw
-
- .. note:: This endpoint is deprecated starting with API version v26.
- Use ``/blinding-prepare`` instead.
-
- Obtain exchange-side input values in preparation for a
- withdraw step for certain denomination cipher types,
- specifically at this point for Clause-Schnorr blind
- signatures.
-
- **Request:** The request body must be a `WithdrawPrepareRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The request was successful, and the response is a `WithdrawPrepareResponse`. Note that repeating exactly the same request
- will again yield the same response (assuming none of the denomination is expired).
- :http:statuscode:`404 Not found`:
- The denomination key is not known to the exchange.
- :http:statuscode:`410 Gone`:
- The requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked. The response is a
- `DenominationGoneMessage`. Clients must evaluate
- the error code provided to understand which of the
- cases this is and handle it accordingly.
-
- **Details:**
-
- .. ts:def:: WithdrawPrepareRequest
-
- interface WithdrawPrepareRequest {
-
- // Nonce to be used by the exchange to derive
- // its private inputs from. Must not have ever
- // been used before.
- nonce: CSNonce;
-
- // Hash of the public key of the denomination the
- // request relates to.
- denom_pub_hash: HashCode;
-
- }
-
- .. ts:def:: WithdrawPrepareResponse
-
- type WithdrawPrepareResponse =
- | ExchangeWithdrawValue;
-
- .. ts:def:: ExchangeWithdrawValue
-
- type ExchangeWithdrawValue = DenomCipher & (
- | ExchangeRsaWithdrawValue
- | ExchangeCsWithdrawValue
- );
-
- .. ts:def:: DenomCipher
-
- interface DenomCipher {
- cipher: "RSA" | "CS";
- }
-
- .. ts:def:: ExchangeRsaWithdrawValue
-
- interface ExchangeRsaWithdrawValue extends DenomCipher {
- cipher: "RSA";
- }
-
- .. ts:def:: ExchangeCsWithdrawValue
-
- interface ExchangeCsWithdrawValue extends DenomCipher {
- cipher: "CS";
-
- // CSR R0 value
- r_pub_0: CSRPublic;
-
- // CSR R1 value
- r_pub_1: CSRPublic;
- }
-
-
-.. http:post:: /reserves/$RESERVE_PUB/batch-withdraw
-
- .. note::
- This endpoint becomes deprecated starting with API version v26.
- Use the ``/withdraw`` endpoint instead, see `withdraw`_.
-
- Withdraw multiple coins from the same reserve. Note that the client should
- commit all of the request details, including the private key of the coins and
- the blinding factors, to disk *before* issuing this request, so that it can
- recover the information if necessary in case of transient failures, like
- power outage, network outage, etc.
-
- **Request:** The request body must be a `BatchWithdrawRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The request was successful, and the response is a `WithdrawResponse`.
- Note that repeating exactly the same request will again yield the same
- response, so if the network goes down during the transaction or before the
- client can commit the coin signature to disk, the coin is not lost.
- :http:statuscode:`403 Forbidden`:
- A signature is invalid.
- This response comes with a standard `ErrorDetail` response.
- :http:statuscode:`404 Not found`:
- A denomination key or the reserve are not known to the exchange. If the
- denomination key is unknown, this suggests a bug in the wallet as the
- wallet should have used current denomination keys from ``/keys``.
- In this case, the response will be a `DenominationUnknownMessage`.
- If the reserve is unknown, the wallet should not report a hard error yet, but
- instead simply wait for up to a day, as the wire transaction might simply
- not yet have completed and might be known to the exchange in the near future.
- In this case, the wallet should repeat the exact same request later again
- using exactly the same blinded coin.
- :http:statuscode:`409 Conflict`:
- One of the following reasons occured:
-
- 1. The balance of the reserve is not sufficient to withdraw the coins of the
- indicated denominations. The response is `WithdrawError` object.
-
- 2. The reserve has a birthday set and requires a request to ``/age-withdraw`` instead.
- The response comes with a standard `ErrorDetail` response with error-code
- ``TALER_EC_EXCHANGE_RESERVES_AGE_RESTRICTION_REQUIRED``
- and an additional field ``maximum_allowed_age`` for the maximum age (in years)
- that the client can commit to in the call to ``/age-withdraw``
- :http:statuscode:`410 Gone`:
- A requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked.
- The response is a `DenominationGoneMessage`. Clients must evaluate the
- error code provided to understand which of the cases this is and handle it
- accordingly.
- :http:statuscode:`451 Unavailable for Legal Reasons`:
- This reserve has received funds from a purse or the amount withdrawn
- exceeds another legal threshold and thus the reserve must
- be upgraded to an account (with KYC) before the withdraw can
- complete. Note that this response does NOT affirm that the
- withdraw will ultimately complete with the requested amount.
- The user should be redirected to the provided location to perform
- the required KYC checks to open the account before withdrawing.
- Afterwards, the request should be repeated.
- The response will be an `LegitimizationNeededResponse` object.
-
- Implementation note: internally, we need to
- distinguish between upgrading the reserve to an
- account (due to P2P payment) and identifying the
- owner of the origin bank account (due to exceeding
- the withdraw amount threshold), as we need to create
- a different payto://-URI for the KYC check depending
- on the case.
-
-
- **Details:**
-
- .. ts:def:: BatchWithdrawRequest
-
- interface BatchWithdrawRequest {
- // Array of requests for the individual coins to withdraw.
- planchets: SingleWithdrawRequest[];
-
- }
-
- .. ts:def:: SingleWithdrawRequest
-
- interface SingleWithdrawRequest {
- // Hash of a denomination public key, specifying the type of coin the client
- // would like the exchange to create.
- denom_pub_hash: HashCode;
-
- // Coin's blinded public key, should be (blindly) signed by the exchange's
- // denomination private key.
- coin_ev: CoinEnvelope;
-
- // Signature of `TALER_SingleWithdrawRequestPS` created with
- // the `reserves's private key <reserve-priv>`
- // using purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW``.
- reserve_sig: EddsaSignature;
-
- }
-
-
------
Reveal
------
-.. note::
- These endpoints are available starting with API version v27.
-
These endpoints are called by the client
#. after a call to `melt`_.
@@ -2123,9 +1938,14 @@ These endpoints are called by the client
The request body is a `RevealMeltRequest`.
+ This endpoint was introduced in this form in protocol **v27**.
+
:http:statuscode:`200 OK`:
The coin's' secret material matched the commitment and the original request was well-formed.
The response body is a `RevealResponse`.
+ :http:statuscode:`403 Forbidden`:
+ One of the signatures is invalid.
+ This response comes with a standard `ErrorDetail` response.
:http:statuscode:`404 Not found`:
The provided commitment is unknown.
:http:statuscode:`409 Conflict`:
@@ -2201,6 +2021,8 @@ These endpoints are called by the client
The request body is a `RevealWithdrawRequest`.
+ This endpoint was introduced in this form in protocol **v27**.
+
:http:statuscode:`200 OK`:
The coin's' secret material matched the commitment and the original request was well-formed.
The response body is a `RevealResponse`.
@@ -2355,7 +2177,6 @@ Reserve History
// Union discriminated by the "type" field.
type TransactionHistoryItem =
| AccountSetupTransaction
- | ReserveBatchWithdrawTransaction
| ReserveWithdrawTransaction
| ReserveCreditTransaction
| ReserveClosingTransaction
@@ -2393,39 +2214,6 @@ Reserve History
}
- .. ts:def:: ReserveBatchWithdrawTransaction
-
- // NOTE: This interface is will be phased out after v26
- // of the protocol
- interface ReserveBatchWithdrawTransaction {
- type: "BATCH_WITHDRAW";
-
- // Offset of this entry in the reserve history.
- // Useful to request incremental histories via
- // the "start" query parameter.
- history_offset: Integer;
-
- // Amount withdrawn.
- amount: Amount;
-
- // Hash of the denomination public key of the coin.
- h_denom_pub: HashCode;
-
- // Hash of the blinded coin to be signed.
- h_coin_envelope: HashCode;
-
- // Signature over a `TALER_SingleWithdrawRequestPS`
- // with purpose ``TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW``
- // created with the reserve's private key.
- reserve_sig: EddsaSignature;
-
- // The maximum age committed to, if applicable
- max_age?: Integer;
-
- // Fee that is charged for withdraw.
- withdraw_fee: Amount;
- }
-
.. ts:def:: ReserveWithdrawTransaction
interface ReserveWithdrawTransaction {
@@ -3423,11 +3211,6 @@ proof to the seller for the escrow of sufficient fund.
// EdDSA public key of a coin being double-spent.
coin_pub: EddsaPublicKey;
- // Transaction history for the coin that is
- // being double-spended.
- // DEPRECATED! Will be removed soon. Use
- // GET /coins/$COIN_PUB to get the history!
- history: CoinSpendHistoryItem[];
}
@@ -3442,83 +3225,9 @@ The refreshing API can be used by wallets to melt partially spent coins, making
transactions with the freshly exchangeed coins unlinkabe to previous transactions
by anyone except the wallet itself.
-However, the new coins are linkable from the private keys of all old coins
-using the ``/refresh/link`` request. While ``/refresh/link`` must be implemented by
-the exchange to achieve taxability, wallets do not really ever need that part of
-the API during normal operation.
-
-
-.. http:post:: /csr-melt
-
- Obtain exchange-side input values in preparation for a
- melt step for certain denomination cipher types,
- specifically at this point for Clause-Schnorr blind
- signatures.
-
- **Request:** The request body must be a `MeltPrepareRequest` object.
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- The request was successful, and the response is a `MeltPrepareResponse`. Note that repeating exactly the same request
- will again yield the same response (assuming none of the denomination is expired).
- :http:statuscode:`404 Not found`:
- A denomination key is not known to the exchange.
- :http:statuscode:`410 Gone`:
- A requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked. The response is a
- `DenominationGoneMessage`. Clients must evaluate
- the error code provided to understand which of the
- cases this is and handle it accordingly.
-
- **Details:**
-
- .. ts:def:: MeltPrepareRequest
-
- interface MeltPrepareRequest {
-
- // Master seed for the Clause-schnorr R-value
- // creation.
- // Must not have been used in any prior request.
- rms: RefreshMasterSeed;
-
- // Array of denominations and coin offsets for
- // each of the fresh coins with a CS-cipher
- // denomination.
- nks: MeltPrepareDenomNonce[];
-
- }
-
- .. ts:def:: MeltPrepareDenomNonce
-
- interface MeltPrepareDenomNonce {
-
- // Offset of this coin in the list of
- // fresh coins. May not match the array offset
- // as the fresh coins may include non-CS
- // denominations as well.
- coin_offset: Integer;
-
- // Hash of the public key of the denomination the
- // request relates to. Must be a CS denomination type.
- denom_pub_hash: HashCode;
- }
-
-
- .. ts:def:: MeltPrepareResponse
-
- interface MeltPrepareResponse {
- // Responses for each request, in the same
- // order that was used in the request.
- ewvs: ExchangeWithdrawValue[];
- }
-
.. _melt:
.. http:post:: /melt
- .. note::
- This endpoint will become available starting with version v27 of the API.
-
"Melts" a coin. Invalidates the coins and prepares for exchanging of fresh
coins. Taler uses a global parameter ``kappa`` for the cut-and-choose
component of the protocol, for which this request is the commitment. Thus,
@@ -3529,6 +3238,8 @@ the API during normal operation.
exchange. The exchange MUST return a 307 or 308 redirection to the correct
base URL if this is the case.
+ This endpoint was introduced in this form in protocol **v27**.
+
:http:statuscode:`200 OK`:
The request was successful. The response body is `MeltResponse` in this case.
:http:statuscode:`403 Forbidden`:
@@ -3572,9 +3283,16 @@ the API during normal operation.
old_denom_sig: DenominationSignature;
// Amount of the value of the old coin that should be melted as part of
- // this refresh operation, including melting fee.
+ // this refresh operation, including melting fee. I.e.:
+ // melting fee of the old coin
+ // + sum over all values of fresh coins
+ // + sum over all withdraw fees for the fresh coins
value_with_fee: Amount;
+ // Seed from which the nonces for the n*κ coin candidates are derived
+ // from.
+ refresh_seed: HashCode;
+
// Master seed for the Clause-Schnorr R-value
// creation. Must match the /blinding-prepare request.
// Must not have been used in any prior melt request.
@@ -3586,11 +3304,11 @@ the API during normal operation.
// for the new coins to order.
denoms_h: HashCode[];
- // Array of ``n`` entries with ``kappa`` many blinded coin candidates,
- // matching the respective entries in ``denoms_h``.
- coin_evs: CoinEnvelope[][kappa];
+ // ``kappa`` arrays of ``n`` entries for blinded coin candidates,
+ // each matching the respective entries in ``denoms_h``.
+ coin_evs: CoinEnvelope[kappa][];
- // Signature by the `coin <coin-priv>` over `TALER_MeltCommitmentPS`.
+ // Signature by the `coin <coin-priv>` over `TALER_RefreshMeltCoinAffirmationPS`.
confirm_sig: EddsaSignature;
}
@@ -3634,7 +3352,6 @@ the API during normal operation.
}
-
.. ts:def:: MeltForbiddenResponse
interface MeltForbiddenResponse {
@@ -3651,258 +3368,6 @@ the API during normal operation.
}
-.. _pre26refresh:
-.. http:post:: /coins/$COIN_PUB/melt
-
- .. note::
- This endpoint will become depreciated starting with version v27 of the API.
- Use ``/melt`` instead, see `melt`_.
-
- "Melts" a coin. Invalidates the coins and prepares for exchanging of fresh
- coins. Taler uses a global parameter ``kappa`` for the cut-and-choose
- component of the protocol, for which this request is the commitment. Thus,
- various arguments are given ``kappa``-times in this step. At present ``kappa``
- is always 3.
-
- The base URL for ``/coins/``-requests may differ from the main base URL of the
- exchange. The exchange MUST return a 307 or 308 redirection to the correct
- base URL if this is the case.
-
- :http:statuscode:`200 OK`:
- The request was successful. The response body is `Pre26MeltResponse` in this case.
- :http:statuscode:`403 Forbidden`:
- One of the signatures is invalid.
- :http:statuscode:`404 Not found`:
- The exchange does not recognize the denomination key as belonging to the exchange,
- or it has expired.
- If the denomination key is unknown, the response will be
- a `DenominationUnknownMessage`.
- :http:statuscode:`409 Conflict`:
- The operation is not allowed as the coin has insufficient
- residual value, or because the same public key of the coin has been
- previously used with a different denomination. Which case it is
- can be decided by looking at the error code
- (``TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS`` or
- ``TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY``).
- The response is `MeltForbiddenResponse` in both cases.
- :http:statuscode:`410 Gone`:
- The requested denomination key is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked. The response is a
- `DenominationGoneMessage`. Clients must evaluate
- the error code provided to understand which of the
- cases this is and handle it accordingly.
-
- **Details:**
-
- .. ts:def:: Pre26MeltRequest
-
- interface Pre26MeltRequest {
-
- // Hash of the denomination public key, to determine total coin value.
- denom_pub_hash: HashCode;
-
- // Signature over the `coin public key <eddsa-coin-pub>` by the denomination.
- denom_sig: DenominationSignature;
-
- // Signature by the `coin <coin-priv>` over the melt commitment.
- confirm_sig: EddsaSignature;
-
- // Amount of the value of the coin that should be melted as part of
- // this refresh operation, including melting fee.
- value_with_fee: Amount;
-
- // Melt commitment. Hash over the various coins to be withdrawn.
- // See also ``TALER_refresh_get_commitment()``.
- rc: HashCode;
-
- // Master seed for the Clause-schnorr R-value
- // creation. Must match the /csr-melt request.
- // Must not have been used in any prior melt request.
- // Must be present if one of the fresh coin's
- // denominations is of type Clause-Schnorr.
- rms?: RefreshMasterSeed;
-
- // IFF the denomination has age restriction support, the client MUST
- // provide the SHA256 hash of the age commitment of the coin.
- // MUST be omitted otherwise.
- age_commitment_hash?: AgeCommitmentHash;
- }
-
- For details about the HKDF used to derive the new coin private keys and
- the blinding factors from ECDHE between the transfer public keys and
- the private key of the melted coin, please refer to the
- implementation in ``libtalerutil``.
-
- .. ts:def:: Pre26MeltResponse
-
- interface Pre26MeltResponse {
- // Which of the ``kappa`` indices does the client not have to reveal.
- noreveal_index: Integer;
-
- // Signature of `TALER_RefreshMeltConfirmationPS` whereby the exchange
- // affirms the successful melt and confirming the ``noreveal_index``.
- exchange_sig: EddsaSignature;
-
- // `Public EdDSA key <sign-key-pub>` of the exchange that was used to generate the signature.
- // Should match one of the exchange's signing keys from ``/keys``. Again given
- // explicitly as the client might otherwise be confused by clock skew as to
- // which signing key was used.
- exchange_pub: EddsaPublicKey;
-
- // Base URL to use for operations on the refresh context
- // (so the reveal operation). If not given,
- // the base URL is the same as the one used for this request.
- // Can be used if the base URL for ``/refreshes/`` differs from that
- // for ``/coins/``, i.e. for load balancing. Clients SHOULD
- // respect the refresh_base_url if provided. Any HTTP server
- // belonging to an exchange MUST generate a 307 or 308 redirection
- // to the correct base URL should a client uses the wrong base
- // URL, or if the base URL has changed since the melt.
- //
- // When melting the same coin twice (technically allowed
- // as the response might have been lost on the network),
- // the exchange may return different values for the ``refresh_base_url``.
- refresh_base_url?: string;
-
- }
-
-
-.. http:post:: /refreshes/$RCH/reveal
-
- .. note::
-
- This endpoint, along with ``/coins/$COIN_PUB/melt``, will become
- depreciated starting with version v27 of the API. Instead, use
- ``/melt`` and ``/reveal-melt``, see `melt`_ and `Reveal`_.
-
- Reveal previously committed values to the exchange, except for the values
- corresponding to the ``noreveal_index`` returned by the ``/coins/``-melt step.
-
- The $RCH is the hash over the refresh commitment from the ``/coins/``-melt step
- (note that the value is calculated independently by both sides and has never
- appeared *explicitly* in the protocol before).
-
- The base URL for ``/refreshes/``-requests may differ from the main base URL of
- the exchange. Clients SHOULD respect the ``refresh_base_url`` returned for the
- coin during melt operations. The exchange MUST return a
- 307 or 308 redirection to the correct base URL if the client failed to
- respect the ``refresh_base_url`` or if the allocation has changed.
-
- Errors such as failing to do proper arithmetic when it comes to calculating
- the total of the coin values and fees are simply reported as bad requests.
- This includes issues such as melting the same coin twice in the same session,
- which is simply not allowed. However, theoretically it is possible to melt a
- coin twice, as long as the ``value_with_fee`` of the two melting operations is
- not larger than the total remaining value of the coin before the melting
- operations. Nevertheless, this is not really useful.
-
- :http:statuscode:`200 OK`:
- The transfer private keys matched the commitment and the original request was well-formed.
- The response body is a `RevealResponse`.
- :http:statuscode:`409 Conflict`:
- There is a problem between the original commitment and the revealed private
- keys. The returned information is proof of the mismatch, and therefore
- rather verbose, as it includes most of the original /refresh/melt request,
- but of course expected to be primarily used for diagnostics.
- The response body is a `RevealConflictResponse`.
- :http:statuscode:`410 Gone`:
- The requested denomination key (for the fresh coins) is not yet or no longer valid.
- It either before the validity start, past the expiration or was revoked. The response is a
- `DenominationGoneMessage`. Clients must evaluate
- the error code provided to understand which of the
- cases this is and handle it accordingly.
-
- **Details:**
-
- Request body contains a JSON object with the following fields:
-
- .. ts:def:: Pre26RevealRequest
-
- interface Pre26RevealRequest {
-
- // Array of ``n`` new hash codes of denomination public keys to order.
- new_denoms_h: HashCode[];
-
- // Array of ``n`` entries with blinded coins,
- // matching the respective entries in ``new_denoms``.
- coin_evs: CoinEnvelope[];
-
- // ``kappa - 1`` transfer private keys (ephemeral ECDHE keys).
- transfer_privs: EddsaPrivateKey[];
-
- // Transfer public key at the ``noreveal_index``.
- transfer_pub: EddsaPublicKey;
-
- // Array of ``n`` signatures made by the wallet using the old coin's private key,
- // used later to verify the /refresh/link response from the exchange.
- // Signs over a `TALER_CoinLinkSignaturePS`.
- link_sigs: EddsaSignature[];
-
- // IFF the corresponding denomination has support for age restriction,
- // the client MUST provide the original age commitment, i. e. the
- // vector of public keys.
- // The size of the vector MUST be the number of age groups as defined by the
- // Exchange in the field ``.age_groups`` of the extension ``age_restriction``.
- old_age_commitment?: Edx25519PublicKey[];
-
- }
-
-
-.. http:get:: /coins/$COIN_PUB/link
-
- Link the old public key of a melted coin to the coin(s) that were exchanged during the refresh operation.
-
- **Request:**
-
- **Response:**
-
- :http:statuscode:`200 OK`:
- All commitments were revealed successfully. The exchange returns an array (typically consisting of only one element), in which each each element of the array contains a `LinkResponse` entry with information about a melting session that the coin was used in.
- :http:statuscode:`404 Not found`:
- The exchange has no linkage data for the given public key, as the coin has not
- yet been involved in a refresh operation.
-
- **Details:**
-
- .. ts:def:: LinkResponse
-
- interface LinkResponse {
- // Transfer ECDHE public key corresponding to the ``coin_pub``, used to
- // compute the blinding factor and private key of the fresh coins.
- transfer_pub: EcdhePublicKey;
-
- // Array with (encrypted/blinded) information for each of the coins
- // exchangeed in the refresh operation.
- new_coins: NewCoinInfo[];
- }
-
- .. ts:def:: NewCoinInfo
-
- interface NewCoinInfo {
- // RSA public key of the exchangeed coin.
- denom_pub: RsaPublicKey;
-
- // Exchange's blinded signature over the fresh coin.
- ev_sig: BlindedDenominationSignature;
-
- // Blinded coin.
- coin_ev : CoinEnvelope;
-
- // Values contributed by the exchange during the
- // withdraw operation (see /csr-melt).
- ewv: ExchangeWithdrawValue;
-
- // Offset of this coin in the refresh operation.
- // Input needed to derive the private key.
- coin_idx: Integer;
-
- // Signature made by the old coin over the refresh request.
- // Signs over a `TALER_CoinLinkSignaturePS`.
- link_sig: EddsaSignature;
-
- }
-
-
------
Recoup
------
@@ -3930,6 +3395,7 @@ in using this API.
Note that the original withdrawal fees will **not** be recouped.
+ .. note:: This endpoint is currently not implemented and the API going to change after **v27**. It is documented here as a placeholder for the documentation of the future endpoint.
**Request:** The request body must be a `RecoupRequest` object.
@@ -4001,6 +3467,38 @@ in using this API.
}
+
+ .. ts:def:: ExchangeWithdrawValue
+
+ type ExchangeWithdrawValue = DenomCipher & (
+ | ExchangeRsaWithdrawValue
+ | ExchangeCsWithdrawValue
+ );
+
+ .. ts:def:: DenomCipher
+
+ interface DenomCipher {
+ cipher: "RSA" | "CS";
+ }
+
+ .. ts:def:: ExchangeRsaWithdrawValue
+
+ interface ExchangeRsaWithdrawValue extends DenomCipher {
+ cipher: "RSA";
+ }
+
+ .. ts:def:: ExchangeCsWithdrawValue
+
+ interface ExchangeCsWithdrawValue extends DenomCipher {
+ cipher: "CS";
+
+ // CSR R0 value
+ r_pub_0: CSRPublic;
+
+ // CSR R1 value
+ r_pub_1: CSRPublic;
+ }
+
.. ts:def:: RecoupWithdrawalConfirmation
interface RecoupWithdrawalConfirmation {
@@ -4022,8 +3520,11 @@ in using this API.
Note that the original refresh fees will **not** be recouped.
+ .. note:: This endpoint is not implemented and the API going to change after **v27**.
+
+ **Request:**
- **Request:** The request body must be a `RecoupRefreshRequest` object.
+ The request body must be a `RecoupRefreshRequest` object.
**Response:**
@@ -4275,12 +3776,6 @@ typically also view the balance.)
// requirement has been evaluated.
requirement_row?: Integer;
- // Current AML state for the target account. Non-zero
- // values indicate that the transfer is blocked due to
- // AML enforcement.
- // Removed in protocol **v20**.
- aml_decision: Integer;
-
// True if the KYC check for the merchant has been
// satisfied. False does not mean that KYC
// is strictly needed, unless also a
@@ -5231,7 +4726,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 `LegitimizationNeededResponse` object (changed in protocol **v20**).
+ The response will be a `LegitimizationNeededResponse` object.
**Details:**
@@ -6214,12 +5709,6 @@ and freeze or unfreeze accounts suspected of money laundering.
// Row ID of the record. Used to filter by offset.
rowid: Integer;
- // Name of the provider
- // which was used to collect the attributes. NULL if they were
- // just uploaded via a form by the account owner.
- // Removed in **v20**.
- provider_name?: 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).
@@ -6231,7 +5720,7 @@ and freeze or unfreeze accounts suspected of money laundering.
}
- .. http:post:: /aml/$OFFICER_PUB/decision
+.. http:post:: /aml/$OFFICER_PUB/decision
Make an AML decision. Triggers the respective action and
records the justification.
diff --git a/core/api-mailbox.rst b/core/api-mailbox.rst
@@ -15,12 +15,13 @@
@author Christian Grothoff
+.. _api-mailbox:
-=======================
-The Mailbox RESTful API
-=======================
+===================
+Mailbox RESTful API
+===================
-This is a proposed API for the GNU Taler Mailbox service which allows Taler
+This is th for the GNU Taler Mailbox service which allows Taler
wallets to securely send push and pull payment requests to other wallets
without having to interact with the respective messaging service.
@@ -66,6 +67,10 @@ Configuration information
// Fixed size of message body.
message_body_bytes: Integer;
+ // Maximum number of messages in a
+ // single response.
+ message_response_limit: Integer;
+
// How long will the service store a message
// before giving up on delivery?
delivery_period: RelativeTime;
diff --git a/core/api-merchant.rst b/core/api-merchant.rst
@@ -292,6 +292,12 @@ Making the payment
This includes the case where the payment is insufficient (sum is below
the required total amount, for example because the wallet calculated the
fees wrong).
+
+ Applicable error codes:
+
+ * ``MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND``: Wallet tried
+ to pay with a non-existent denomination.
+
:http:statuscode:`402 Payment required`:
There used to be a sufficient payment, but due to refunds the amount effectively
paid is no longer sufficient. (If the amount is generally insufficient, we
@@ -311,6 +317,12 @@ Making the payment
(this includes re-using the same coin after a refund),
the response will include the ``exchange_url`` for which the payment failed,
in addition to the response from the exchange to the ``/batch-deposit`` request.
+
+ Applicable error codes:
+
+ * ``MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS``: Exchange reported insufficient
+ funds for one of the coins.
+
:http:statuscode:`410 Gone`:
The offer has expired and is no longer available.
:http:statuscode:`412 Precondition failed`:
diff --git a/core/api-taldir.rst b/core/api-taldir.rst
@@ -17,11 +17,11 @@
@author Martin Schanzenbach
-======================
-The TalDir RESTful API
-======================
+=====================
+Directory RESTful API
+=====================
-This is a proposed API for the TalDir service which allows Taler wallets to
+This is the API for the Taler Directory (TalDir) service which allows Taler wallets to
securely associate an inbox service (URL and public key) with the address of a
messaging service used by the wallet's user. Wallets can also lookup the
inbox of other users. This will enable wallets to make wallet-to-wallet
@@ -30,7 +30,7 @@ address in a messaging service. Examples for messaging services include E-mail
and SMS.
The service in principle allows registration of any valid URI as inbox
service.
-For Taler, the URI must follow the format defined in TODO.
+For Taler, the URI is a link a :ref:`mailbox <api-mailbox>`.
The API specified here follows the :ref:`general conventions <http-common>`
for all details not specified in the individual requests.
diff --git a/deployments/tops.rst b/deployments/tops.rst
@@ -26,8 +26,8 @@ Regulatory requirements are set by `VQF <https://www.vqf.ch/indexen.html>`_
and detailed in their SRO-Regulation document. Our AML processes
are based on their forms ("VQF Document Nr. 902.$x").
-High-Level Processes
---------------------
+Overview of High-Level Processes
+--------------------------------
Establishing a Business Relationship
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -97,7 +97,6 @@ Terminating a Business Relationship
A business relationship is automatically considered terminated if no
transactions have been processed with the GNU Taler system for over 12 months.
-
Credit / Debit Restrictions
---------------------------
@@ -116,8 +115,9 @@ Initial Threshold Rules
* Deposit:
* ``deposit-zero``: 0 CHF => measure ``accept-tos``
- * 2500 CHF per month => measure ``kyx``
- * 15000 CHF per year => measure ``kyx``
+ * Note: While there are no further DEPOSIT rules,
+ the aggregate rules still apply after deposits
+ have been made.
* Aggregate:
@@ -211,6 +211,8 @@ profiles:
Properties
----------
+Properties are registered at the GNU Taler Account Properties `GNU Taler Account Properties <https://git.taler.net/gana.git/tree/gnu-taler-account-properties>`_.
+
* ``FILE_NOTE :: Text``:
* Current note on the GWG file.
@@ -219,39 +221,39 @@ Properties
* Customer name or internal alias.
-* ``AML_ACCOUNT_OPEN :: Boolean``
+* ``ACCOUNT_OPEN :: Boolean``
* Was this customer activated for deposit operations?
* Only set after merchant passes KYC
* We store this to know when to emit the ``(INCR|DECR)_ACCOUNT_OPEN`` and related events
-* ``AML_DOMESTIC_PEP :: Boolean``
+* ``PEP_DOMESTIC :: Boolean``
* Is the customer a domestic PEP?
-* ``AML_FOREIGN_PEP :: Boolean``
+* ``PEP_FOREIGN :: Boolean``
* Is the customer a foreign PEP?
-* ``AML_INTERNATIONAL_ORG_PEP :: Boolean``
+* ``PEP_INTERNATIONAL_ORGANIZATION :: Boolean``
* Is the customer a international org PEP?
-* ``AML_HIGH_RISK_CUSTOMER :: Boolean``
+* ``HIGH_RISK_CUSTOMER :: Boolean``
* Is the customer classified as high-risk?
-* ``AML_HIGH_RISK_COUNTRY :: Boolean``
+* ``HIGH_RISK_COUNTRY :: Boolean``
* Is the customer associated with high-risk (VQF Dok. Nr. 902.4.1) country?
-* ``AML_ACCOUNT_IDLE :: Boolean``
+* ``ACCOUNT_IDLE :: Boolean``
* The account has been marked as idle (typically by a batch process that checks
for idle accounts).
-* ``AML_INVESTIGATION_STATE``
+* ``INVESTIGATION_STATE``
* The MROS reporting state for the account.
* Values:
@@ -268,7 +270,7 @@ Properties
* ``REPORTED_SUSPICION_SIMPLE``: Reported under Art. 305 StGB (German "einfacher Verdacht", simple suspicion)
* ``REPORTED_SUSPICION_SUBSTANTIATED``: Reported under Art. 9 GwG (German "begründeter Verdacht", substantiated suspicion)
-* ``AML_INVESTIGATION_TRIGGER :: Text``
+* ``INVESTIGATION_TRIGGER :: Text``
* Informal reason why the AML investigation was triggered;
examples include suspicious transaction or (automated)
@@ -306,12 +308,12 @@ PEP/Risk classification:
* ``INCR_HIGH_RISK_CUSTOMER`` / ``DECR_HIGH_RISK_CUSTOMER``
* ``INCR_HIGH_RISK_COUNTRY`` / ``INCR_HIGH_RISK_COUNTRY``
* ``INCR_PEP`` / ``DECR_PEP``
-* ``INCR_FOREIGN_PEP`` / ``DECR_FOREIGN_PEP``
-* ``INCR_DOMESTIC_PEP`` / ``DECR_DOMESTIC_PEP``
-* ``INCR_INTERNATIONAL_ORG_PEP`` / ``DECR_INTERNATIONAL_ORG_PEP``
+* ``INCR_PEP_FOREIGN`` / ``DECR_PEP_FOREIGN``
+* ``INCR_PEP_DOMESTIC`` / ``DECR_PEP_DOMESTIC``
+* ``INCR_PEP_INTERNATIONAL_ORGANIZATION`` / ``DECR_PEP_INTERNATIONAL_ORGANIZATION``
-MROS Reporting (see ``AML_INVESTIGATION_STATE`` property):
+MROS Reporting (see ``INVESTIGATION_STATE`` property):
* ``MROS_REPORTED_SUSPICION_SIMPLE``
* ``MROS_REPORTED_SUSPICION_SUBSTANTIATED``
@@ -335,6 +337,238 @@ Implementation notes:
where the context is updated based on documents still required.
+
+Procedural View
+---------------
+
+This section provides a procedural view of the AML processes defined by the rules
+earlier in the document. It is meant to give some further context to the rules
+and show how the rules are used in the context of Taler business processes.
+
+It only takes into account the standard rules. Decisions from the AML
+officer can lead to a deviation from the standard process/rules.
+
+Wallet User: Onboarding and Withdrawal
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+1. User installs the Taler wallet software on their device of choice.
+2. User adds the TOPS Taler Exchange to their Taler wallet
+3. User starts a new withdrawal via the wallet. This creates a new
+ (pending) transaction in the wallet. *Optionally:* If the wallet can deduct
+ that the user has to complete a KYC process for the withdrawal, it notifies
+ the user.
+4. User follows instructions to send money to the TOPS exchange
+5. The wallet waits until the exchange knows about the
+ user's wire transfer.
+6. The user's wallet checks with the exchange whether the withdrawal would
+ cross the balance threshold. The key/identifier for is the wallet ID for
+ the exchange (which is typically the reserve public key for P2P
+ transactions).
+
+ **The TOPS exchange currently has no balance limits set, thus balance limits would
+ never be crossed.**
+
+ * If the balance limit is not crossed (or the user increased the limit via KYC), continue at (7).
+ * If no KYC process is started or the KYC process fails or times out, funds
+ are automatically wired back to the customer after a reserve close
+ timeout. **Done.**
+
+7. The wallet attempts to withdraw electronic cash tokens. The exchange
+ checks the withdrawal limit based on the IBAN that the
+ customer used to transfer CHF to the exchange:
+
+ * If the customer has already successfully completed
+ the ``sms-registration`` or ``postal-registration``,
+ the withdrawal limit is 2500 CHF/month and 15000 CHF/year.
+ * Otherwise, the limit is 200 CHF per month. If this limit would
+ be crossed by the withdrawal, the wallet redirects the user to
+ the exchange's KYC page, where the user can complete the ``sms-registration``
+ or ``postal-registration``.
+ * If no limit would be crossed, continue at (8)
+ * If a limit would be crossed and the customer is not able to
+ lift it via the KYC process, funds are wired back automatically
+ after a reserve close timeout. **Done.**
+
+8. The wallet receives the (blindly signed) tokens from the exchange,
+ the withdrawal is done. **Done.**
+
+
+Wallet User: Deposit of E-Money
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This process applies when the user wants to send CHF in their Taler wallet back
+to their CHF bank account. Technically, it is the same process as the merchant
+accepting a Taler payment. However, it might be treated differently from an
+AML perspective.
+
+1. The user's wallet asks the exchange to deposit a Taler payment
+ to the user's own bank account.
+2. The exchange checks whether the users's public key is associated with the
+ users's bank account specified in the deposit permission.
+
+ Note that by default, the wallet uses a bank account that has
+ previously used for withdrawal. The withdrawal already associates
+ the reserve's public key with the IBAN used for the withdrawal.
+ Thus *usually* the right associated public key is already present.
+
+ * If the association is missing, the exchange rejects the deposit. The
+ customer must do a 1 rappen wire transfer to the exchange with a public
+ key (as shown in the wallet) in the remittance information. **Done.**
+ * Otherwise, continue at (3).
+3. The exchange checks the ``DEPOSIT`` limit of the user. The user is identified via their IBAN.
+
+ * Initally, the deposit limit is CHF 0. The user must accept the exchange's
+ terms of service on the exchange's KYC page to lift this limit to CHF 2500/month
+ and CHF 15000/year
+ * If no deposit limit would be crossed, the exchange accepts the deposit from the user.
+ Continue at (4).
+ * Otherwise the exchange rejects the payment. The response is relayed to the
+ wallet, which can (if necessary) refund coins previously deposited for the
+ same payment and then refresh used coins. **Done.**
+4. After the wire transfer deadline for the deposit has passed, the exchange
+ checks whether the wire transfer would cross the ``AGGREGATE`` threshold for
+ the merchant.
+
+ * Initally, the aggregate limit is CHF 2500/month and CHF 15000/year. If
+ that limit would be crossed, the customer must undergo a KYB process. This
+ KYB process might result in limits being increased, depending on the
+ details of the user.
+ * If no aggregation limit would be crossed, the exchange initiates the wire transfer to the user.
+ * Otherwise the exchange holds the funds until the user completes the necessary AML process.
+
+
+Wallet User: Receiving P2P Payments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Applicable to both receiving P2P payments (push) and getting paid for P2P
+payment requests (pull).*
+
+1. The customer instructs their wallet to accept a P2P payment from another wallet.
+2. The wallet tries to receive the P2P payment.
+ The exchange checks the P2P receive (technically: ``MERGE``)
+ limit, based on the wallet ID.
+
+ * If the customer has successfully completed ``postal-registration`` or ``sms-registration``,
+ the limits are 2500 CHF / month and 15000 CHF / year.
+ * Otherwise, the limit is 0 CHF. The wallet redirects the user to the
+ exchange's KYC page, where the user can complete the ``sms-registration``
+ or ``postal-registration``.
+ * If P2P receive is below the limits (or the customer increases the limits via KYC),
+ the P2P recive can proceed. **Done.**
+ * Otherwise, the P2P payment expires and the sender's wallet reclaims the money. **Done.**
+
+
+
+
+FIXME: Do withdrawal limits also apply for withdrawal from the merge reserve?
+
+Wallet User: Sending P2P Payments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*Applicable to both sending P2P payments (push) and paying for P2P payment
+requests (pull).*
+
+There are no KYC/AML-relevant steps required for
+sending P2P payments.
+
+Merchant: Onboarding
+^^^^^^^^^^^^^^^^^^^^
+
+1. The merchant provisions a Taler merchant backend service.
+2. A keypair is generated (or imported) for the merchant.
+3. The merchant adds their (Swiss) bank account to the merchant backend
+4. The merchant backend checks the KYC status of the account with the exchange.
+5. The exchange checks if the merchant's public key is already associated with
+ the merchant's bank account.
+
+ * If not, the merchant needs to make a payment (1 rappen) to the exchange
+ with the public key in the remittance information. Continue at (4).
+ * Otherwise, continue at (6).
+
+6. If the merchant's bank account still has a deposit limit of zero, the
+ merchant needs to accept the TOPS exchange terms of service on the
+ exchange's KYC page.
+
+7. The deposit rule is lifted and the merchant can start accepting Taler payments from customers.
+ However, initially no aggregated settlement payments (wire transfers)
+ will be send from the exchange to the merchants, until the merchant
+ has completed further KYC steps (``vqf_902_1_customer`` etc.).
+8. Optionally, the merchant can (via a link in the merchant backend to the KYC page)
+ and immediately complete the further KYC process steps.
+
+Merchant: Receiving Payments from Wallets
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1. The merchant receives a Taler payment (technically: deposit permissions) from a
+ wallet.
+2. The merchant asks the exchange to deposit the Taler payment.
+3. The exchange checks whether the merchant's public key is associated with the
+ merchant's bank account specified (as a salted hash) in the deposit
+ permission.
+
+ * If the association is missing, the exchange rejects the deposit. **Done.**
+ * Otherwise, continue at (4).
+
+4. The exchange checks the ``DEPOSIT`` limit of the merchant.
+ The merchant is identified via their IBAN.
+
+ * Initally, the deposit limit is CHF 0. The merchant must accept the exchange's
+ terms of service on the exchange's KYC page to lift this limit to CHF 2500/month
+ and CHF 15000/year
+ * If the merchant has accepted the terms of service, the deposit limit
+ is CHF 2500/month and CHF 15000/year. If that limit
+ is crossed, the merchant must undergo a KYB process. This KYB
+ process might result in limits being increased, depending
+ on the details of the business.
+ * If no deposit limit would be crossed, the exchange accepts the deposit from the merchant. **Done.**
+ * Otherwise the exchange rejects the payment. The response is relayed to the
+ wallet, which can (if necessary) refund coins previously deposited for the
+ same payment and then refresh used coins. **Done.**
+
+
+Merchant: Receiving Wire Transfers for Taler Payments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+1. The merchange receives payments from wallets.
+2. The exchange waits and aggregates payments until the first wire transfer
+ deadline set by the merchant has passed.
+3. The exchange checks whether the aggregated wire transfer would cross the
+ ``AGGREGATE`` threshold for the merchant.
+
+ * Initally, the aggregate limit is CHF 2500/month and CHF 15000/year. If
+ that limit would be crossed, the merchant must undergo a KYB process. This
+ KYB process might result in limits being increased, depending on the
+ details of the business.
+ * If no aggregation limit would be crossed, the exchange initiates the wire transfer to the merchant.
+ * Otherwise the exchange holds the funds until the merchant completes the necessary AML process.
+
+KYC Providers
+-------------
+
+challenger-postal
+^^^^^^^^^^^^^^^^^
+
+**Purpose:** Validate customer address via postal mail.
+
+**Attributes**
+
+.. code:: none
+
+ CONTACT_NAME :: Text
+ ADDRESS_LINES: Text
+ ADDRESS_COUNTRY :: "CH"
+
+* ``CONTACT_NAME``
+
+ **Description:** Name of the person or company whose address was validated.
+
+* ``ADDRESS_LINES``
+
+ **Description:** Contact address (without name and country). May span
+ over multiple lines (separated by newline characters).
+
+* ``ADDRESS_COUNTRY``
+
+ **Description:** Country of the validated address. Only "CH" is allowed.
+
AML/KYC Forms
-------------
@@ -342,6 +576,34 @@ The following subsections define the contents of the forms. The corresponding
field names are registered via `GANA <https://git.taler.net/gana.git/tree/gnu-taler-form-attributes>`_.
The the UI for the forms is defined in `taler-typescript-core <https://git.taler.net/taler-typescript-core.git/tree/packages/web-util/src/forms/gana>`_
+When the customer or officer submit the information throught the client software it must
+include the fields FORM_ID and FORM_VERSION attributed as defined in GANA.
+
+
+accept-tos
+^^^^^^^^^^
+
+**Filled out by:** Customer
+
+**Purpose:** Customer confirms that they accept the terms of service.
+
+**Form Demo:** `Link <https://www.taler.net/files/storybook-forms/stories.html#forms-accept%20tos-EmptyForm>`_
+
+**Attributes**:
+
+.. code:: none
+
+ ACCEPTED_TERMS_OF_SERVICE :: Text
+ DOWNLOADED_TERMS_OF_SERVICE :: Boolean
+
+* ``ACCEPTED_TERMS_OF_SERVICE``
+
+ * **Description**: ToS version that the user accepted.
+
+* ``DOWNLOADED_TERMS_OF_SERVICE``
+
+ * **Description**: Whether the user downloaded the
+ terms of service.
generic_note
^^^^^^^^^^^^
@@ -1634,42 +1896,42 @@ Properties:
.. code:: javascript
- newProps.AML_ACCOUNT_OPEN = true;
+ newProps.ACCOUNT_OPEN = true;
Events:
.. code:: javascript
- if (propBecameTrue(AML_ACCOUNT_OPEN)) {
+ if (propBecameTrue(ACCOUNT_OPEN)) {
emit(INCR_ACCOUNT_OPEN);
const isPep = (
- newProps.AML_FOREIGN_PEP ||
- newProps.AML_DOMESTIC_PEP ||
- newProps.AML_INTERNATIONAL_ORG_PEP
+ newProps.PEP_FOREIGN ||
+ newProps.PEP_DOMESTIC ||
+ newProps.PEP_INTERNATIONAL_ORGANIZATION
);
if (isPep) {
emit(INCR_PEP);
}
- if (newProps.AML_FOREIGN_PEP) {
- emit(INCR_FOREIGN_PEP);
+ if (newProps.PEP_FOREIGN) {
+ emit(INCR_PEP_FOREIGN);
}
- if (newProps.AML_DOMESTIC_PEP) {
- emit(INCR_DOMESTIC_PEP);
+ if (newProps.PEP_DOMESTIC) {
+ emit(INCR_PEP_DOMESTIC);
}
- if (newProps.AML_INTERNATIONAL_ORG_PEP) {
- emit(INCR_INTERNATIONAL_ORG_PEP);
+ if (newProps.PEP_INTERNATIONAL_ORGANIZATION) {
+ emit(INCR_PEP_INTERNATIONAL_ORGANIZATION);
}
- if (newProps.AML_HIGH_RISK_CUSTOMER) {
+ if (newProps.HIGH_RISK_CUSTOMER) {
emit(INCR_HIGH_RISK_CUSTOMER);
}
- if (newProps.AML_HIGH_RISK_COUNTRY) {
+ if (newProps.HIGH_RISK_COUNTRY) {
emit(INCR_HIGH_RISK_COUNTRY);
}
}
@@ -1682,53 +1944,53 @@ Properties:
.. code:: javascript
- newProps.AML_FOREIGN_PEP = form.PEP_FOREIGN;
- newProps.AML_DOMESTIC_PEP = form.PEP_DOMESTIC;
- newProps.AML_INTERNATIONAL_ORG_PEP = form.PEP_INTERNATIONAL_ORGANIZATION;
- newProps.AML_HIGH_RISK_CUSTOMER = form.RISK_CLASSIFICATION_LEVEL == "HIGH_RISK";
- newProps.AML_HIGH_RISK_COUNTRY = form.COUNTRY_RISK_NATIONALITY_LEVEL == "HIGH";
+ newProps.PEP_FOREIGN = form.PEP_FOREIGN;
+ newProps.PEP_DOMESTIC = form.PEP_DOMESTIC;
+ newProps.PEP_INTERNATIONAL_ORGANIZATION = form.PEP_INTERNATIONAL_ORGANIZATION;
+ newProps.HIGH_RISK_CUSTOMER = form.RISK_CLASSIFICATION_LEVEL == "HIGH_RISK";
+ newProps.HIGH_RISK_COUNTRY = form.COUNTRY_RISK_NATIONALITY_LEVEL == "HIGH";
Events:
.. code:: javascript
- if (oldProps.AML_ACCOUNT_OPEN) {
- if (propBecameTrue(AML_FOREIGN_PEP) {
- emit(INCR_FOREIGN_PEP);
+ if (oldProps.ACCOUNT_OPEN) {
+ if (propBecameTrue(PEP_FOREIGN) {
+ emit(INCR_PEP_FOREIGN);
}
- if (propBecameTrue(AML_DOMESTIC_PEP) {
- emit(INCR_INTERNATIONAL_ORG_PEP);
+ if (propBecameTrue(PEP_DOMESTIC) {
+ emit(INCR_PEP_INTERNATIONAL_ORGANIZATION);
}
- if (propBecameTrue(AML_DOMESTIC_PEP) {
- emit(INCR_DOMESTIC_PEP);
+ if (propBecameTrue(PEP_DOMESTIC) {
+ emit(INCR_PEP_DOMESTIC);
}
- if (propBecameFalse(AML_FOREIGN_PEP) {
- emit(DECR_FOREIGN_PEP);
+ if (propBecameFalse(PEP_FOREIGN) {
+ emit(DECR_PEP_FOREIGN);
}
- if (propBecameFalse(AML_DOMESTIC_PEP) {
- emit(DECR_INTERNATIONAL_ORG_PEP);
+ if (propBecameFalse(PEP_DOMESTIC) {
+ emit(DECR_PEP_INTERNATIONAL_ORGANIZATION);
}
- if (propBecameFalse(AML_DOMESTIC_PEP) {
- emit(DECR_DOMESTIC_PEP);
+ if (propBecameFalse(PEP_DOMESTIC) {
+ emit(DECR_PEP_DOMESTIC);
}
const wasPep = (
- oldProps.AML_DOMESTIC_PEP ||
- oldProps.AML_FOREIGN_PEP ||
- oldProps.AML_INTERNATIONAL_ORG_PEP);
+ oldProps.PEP_DOMESTIC ||
+ oldProps.PEP_FOREIGN ||
+ oldProps.PEP_INTERNATIONAL_ORGANIZATION);
const isPep = (
- newProps.AML_DOMESTIC_PEP ||
- newProps.AML_FOREIGN_PEP ||
- newProps.AML_INTERNATIONAL_ORG_PEP);
+ newProps.PEP_DOMESTIC ||
+ newProps.PEP_FOREIGN ||
+ newProps.PEP_INTERNATIONAL_ORGANIZATION);
if (wasPep && !isPep) {
emit(DECR_PEP);
}
if (!wasPep & isPep) {
emit(INCR_PEP);
}
- if (propBecameTrue(AML_HIGH_RISK)) {
+ if (propBecameTrue(HIGH_RISK)) {
emit(INCR_HIGH_RISK);
}
- if (propBecameFalse(AML_HIGH_RISK)) {
+ if (propBecameFalse(HIGH_RISK)) {
emit(DECR_HIGH_RISK);
}
}
@@ -1743,16 +2005,16 @@ Properties:
if (INCRISK_RESULT == "SIMPLE_SUSPICION") {
- newProps.AML_INVESTIGATION_STATE = "REPORTED_SUSPICION_SIMPLE";
+ newProps.INVESTIGATION_STATE = "REPORTED_SUSPICION_SIMPLE";
} else if (INCRISK_RESULT == "SUBSTANTIATED_SUSPICION") {
- newProps.AML_INVESTIGATION_STATE = "REPORTED_SUSPICION_SUBSTANTIATED";
+ newProps.INVESTIGATION_STATE = "REPORTED_SUSPICION_SUBSTANTIATED";
} else if (INCRISK_RESULT == "NO_SUSPICION") {
- newProps.AML_INVESTIGATION_STATE = "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION";
+ newProps.INVESTIGATION_STATE = "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION";
} else if (INCRISK_RESULT == "OTHER") {
// FIXME-#9677: would be nice if we instead could set the property to "undefined"/null
// and *force* the AML officer to manually set it.
// Alternatively, we should probably default to "INVESTIGATION_PENDING". -CG
- newProps.AML_INVESTIGATION_STATE = "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION";
+ newProps.INVESTIGATION_STATE = "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION";
} else {
not_reached();
}
@@ -1761,15 +2023,15 @@ Events:
.. code:: javascript
- if (oldProps.AML_INVESTIGATION_STATE == "NONE" ||
- oldProps.AML_INVESTIGATION_STATE == "INVESTIGATION_PENDING" ||
- oldProps.AML_INVESTIGATION_STATE == null) {
- if (newProps.AML_INVESTIGATION_STATE == "REPORTED_SUSPICION_SIMPLE" ||
- newProps.AML_INVESTIGATION_STATE == "REPORTED_SUSPICION_SUBSTANTIATED" ||
- newProps.AML_INVESTIGATION_STATE == "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION") {
+ if (oldProps.INVESTIGATION_STATE == "NONE" ||
+ oldProps.INVESTIGATION_STATE == "INVESTIGATION_PENDING" ||
+ oldProps.INVESTIGATION_STATE == null) {
+ if (newProps.INVESTIGATION_STATE == "REPORTED_SUSPICION_SIMPLE" ||
+ newProps.INVESTIGATION_STATE == "REPORTED_SUSPICION_SUBSTANTIATED" ||
+ newProps.INVESTIGATION_STATE == "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION") {
emit(INCR_INVESTIGATION_CONCLUDED);
}
- if (newProps.AML_INVESTIGATION_STATE == "REPORTED_SUSPICION_SUBSTANTIATED") {
+ if (newProps.INVESTIGATION_STATE == "REPORTED_SUSPICION_SUBSTANTIATED") {
// FIXME-#9676: if possible, we should force the AML officer to tick
// an extra check-box "I submitted this case to MROS". No need to
// actually do anything here server-side, it's more an explicit
@@ -1777,7 +2039,7 @@ Events:
// emitted if the report was files.
emit(MROS_REPORTED_SUSPICION_SUBSTANTIATED);
}
- if (newProps.AML_INVESTIGATION_STATE == "REPORTED_SUSPICION_SIMPLE") {
+ if (newProps.INVESTIGATION_STATE == "REPORTED_SUSPICION_SIMPLE") {
// FIXME-#9676: if possible, we should force the AML officer to tick
// an extra check-box "I submitted this case to MROS". No need to
// actually do anything here server-side, it's more an explicit
@@ -1909,7 +2171,7 @@ Based on this, we have the following statistics:
* Of those PEPs, how many were with *foreign* PEPs? (self-declaration 4.3.)
- * Implementation: ``evtcount(INCR_FOREIGN_PEP, start=0, end=dec_last_20xx) - evtcount(DECR_FOREIGN_PEP, start=0, end=dec_last_20xx)``
+ * Implementation: ``evtcount(INCR_PEP_FOREIGN, start=0, end=dec_last_20xx) - evtcount(DECR_PEP_FOREIGN, start=0, end=dec_last_20xx)``
* Number of other additional (other than PEPs and foreign PEPs) high-risk business relationships in 20XX (self-declaration 4.4.)
@@ -2001,9 +2263,9 @@ Three properties are set:
indicates how confident we are that the rating is accurate, with 0
indicating no data available, and 1 indicating that all possible
fields could be evaluated
-* ``AML_INVESTIGATION_STATE`` is set to ``INVESTIGATION_PENDING``
+* ``INVESTIGATION_STATE`` is set to ``INVESTIGATION_PENDING``
if the rating and confidence are sufficiently high
-* ``AML_INVESTIGATION_TRIGGER`` is set to ``SANCTION_LIST_MATCH``
+* ``INVESTIGATION_TRIGGER`` is set to ``SANCTION_LIST_MATCH``
Finally, sanction list hits trigger one of two possible events:
diff --git a/screenshots/cta-wire-transfer-details-android-1.png b/screenshots/cta-wire-transfer-details-android-1.png
Binary files differ.
diff --git a/screenshots/cta-wire-transfer-details-android-latest.png b/screenshots/cta-wire-transfer-details-android-latest.png
@@ -1 +1 @@
-cta-wire-transfer-details-android-0.png
-\ No newline at end of file
+cta-wire-transfer-details-android-1.png
+\ No newline at end of file
diff --git a/screenshots/cta-withdraw-android-2.png b/screenshots/cta-withdraw-android-2.png
Binary files differ.
diff --git a/screenshots/cta-withdraw-android-latest.png b/screenshots/cta-withdraw-android-latest.png
@@ -1 +1 @@
-cta-withdraw-android-1.png
-\ No newline at end of file
+cta-withdraw-android-2.png
+\ No newline at end of file
diff --git a/screenshots/cta-withdraw-review-android-2.png b/screenshots/cta-withdraw-review-android-2.png
Binary files differ.
diff --git a/screenshots/cta-withdraw-review-android-latest.png b/screenshots/cta-withdraw-review-android-latest.png
@@ -1 +1 @@
-cta-withdraw-review-android-1.png
-\ No newline at end of file
+cta-withdraw-review-android-2.png
+\ No newline at end of file
diff --git a/taler-developer-manual.rst b/taler-developer-manual.rst
@@ -1,7 +1,7 @@
..
This file is part of GNU TALER.
- Copyright (C) 2014-2023 Taler Systems SA
+ Copyright (C) 2014-2025 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
@@ -488,9 +488,6 @@ All Taler components must be tagged with git before they are deployed on the
DD = day
SS = serial
-.. include:: checklists/checklist-demo-upgrade.rst
-
-
Environments and Builders on taler.net
======================================
diff --git a/taler-wallet.rst b/taler-wallet.rst
@@ -14,8 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-Wallet Manual
-#############
+Wallet Developer Manual
+#######################
The GNU Taler wallet allows customers to withdraw and spend digital cash.
@@ -167,7 +167,7 @@ Resetting the wallet
--------------------
The wallet can be reset by deleting its database file. By default, the database file
-is ``$HOME/.talerwalletdb.json``.
+is ``$HOME/.talerwalletdb.sqlite3``.
Handling taler:// URIs
@@ -274,50 +274,6 @@ is functional:
# The "deposit group id" can be found in the output of the transactions list.
$ taler-wallet-cli deposit track $DEPOSIT_GROUP_ID
-.. _withdraw-simulation:
-
-
-Paying for an order
-===================
-
-.. note::
-
- This section is in dire need for some editing...
-
-This section explains how to pay for an order in a scenario where the fiat bank
-is simulated. The simulation takes place by crafting ad-hoc XML files as if the
-bank would have issued them. Such XML files carry information about incoming payments
-to the regional currency master bank account. Finally, the XML files are passed
-to LibEuFin nexus via a convenient CLI method. The responsible script for such
-simulation is ``withdraw.sh``.
-
-Run ``./withdraw.sh`` without any arguments. Assuming that you ran the command
-as the ``test-user``, after the execution, 5 units of the regional currency should
-be found in the CLI wallet owned by ``test-user``.
-
-
-Check it with:
-
-.. code-block:: console
-
- $ taler-wallet-cli balance
-
-If so, call the wallet in the following way to finally pay for the order just created:
-
-.. code-block:: console
-
- $ taler-wallet-cli handle-uri "$TALER_PAY_URI"
-
-.. note::
-
- Reset the state before going to production, as it impacts the way nexus
- asks records to the bank. In particular, delete: any database and the
- files ``config/user.conf`` and ``config/internal.conf``, and finally run
- ``./main.sh`` again.
-
-
-
-
APIs and Data Formats
=====================
@@ -415,7 +371,7 @@ Integration Test Example
------------------------
Integration tests can be done with the low-level wallet commands. To select which coins and denominations
-to use, the wallet can dump the coins in an easy-to-process format (`CoinDumpJson <https://git.taler.net/wallet-core.git/tree/src/types/talerTypes.ts#n734>`__).
+to use, the wallet can dump the coins in an easy-to-process format (`CoinDumpJson <https://git.taler.net/taler-typescript-core.git/tree/packages/taler-util/src/types-taler-wallet.ts#n613>`__).
The database file for the wallet can be selected with the ``--wallet-db``
option. This option must be passed to the ``taler-wallet-cli`` command and not
@@ -426,12 +382,12 @@ The following example does a simple withdrawal recoup:
.. code-block:: console
# Withdraw digital cash
- $ taler-wallet-cli --wallet-db=mydb.json testing withdraw \
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 testing withdraw \
-b https://bank.int.taler.net/ \
-e https://exchange.int.taler.net/ \
-a INTKUDOS:10
- $ coins=$(taler-wallet-cli --wallet-db=mydb.json advanced dump-coins)
+ $ coins=$(taler-wallet-cli --wallet-db=mydb.sqlite3 advanced dump-coins)
# Find coin we want to revoke
$ rc=$(echo "$coins" | \
@@ -450,29 +406,29 @@ The following example does a simple withdrawal recoup:
$ taler-deployment-restart
# Now we suspend the other coins, so later we will pay with the recouped coin
- $ taler-wallet-cli --wallet-db=mydb.json advanced suspend-coins "$susp"
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 advanced suspend-coins "$susp"
# Update exchange /keys so recoup gets scheduled
- $ taler-wallet-cli --wallet-db=mydb.json exchanges update -f https://exchange.int.taler.net/
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 exchanges update -f https://exchange.int.taler.net/
# Block until scheduled operations are done
- $ taler-wallet-cli --wallet-db=mydb.json run-until-done
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 run-until-done
# Now we buy something, only the coins resulting from recouped will be
# used, as other ones are suspended
- $ taler-wallet-cli --wallet-db=mydb.json testing test-pay \
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 testing test-pay \
-m https://backend.int.taler.net/ \
-k sandbox \
-a "INTKUDOS:1" \
-s "foo"
- $ taler-wallet-cli --wallet-db=mydb.json run-until-done
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 run-until-done
To test refreshing, force a refresh:
.. code-block:: console
- $ taler-wallet-cli --wallet-db=mydb.json advanced force-refresh "$coin_pub"
+ $ taler-wallet-cli --wallet-db=mydb.sqlite3 advanced force-refresh "$coin_pub"
To test zombie coins, use the timetravel option. It **must** be passed to the
@@ -481,59 +437,9 @@ top-level command and not the subcommand:
.. code-block:: console
# Update exchange /keys with time travel, value in microseconds
- $ taler-wallet-cli --timetravel=1000000 --wallet-db=mydb.json \
+ $ taler-wallet-cli --timetravel=1000000 --wallet-db=mydb.sqlite3 \
exchanges update -f https://exchange.int.taler.net/
-Test Cases
-----------
-
-Things we already have tests for:
-
-* Can the wallet recoup coins and spend them?
- [`link <https://git.taler.net/wallet-core.git/tree/integrationtests/test-recoup.sh>`__]
-
-Things we still need tests for:
-
-* Does the wallet do retries correctly when the exchange is not reachable?
- Or when the merchant is not reachable? Or the bank?
- This can be tested by temporarily killing those services.
-* How does the wallet deal with processing the same ``taler://(pay|withdraw)`` URI twice?
-* Test refunds
-* Test for :ref:`session-based payments <repurchase>`
-* Test case for auto-refunds
- (scenario where the vending machine finds out that its motor is broken,
- so it automatically gives a refund)
-* Does the wallet report "insufficient balance" correctly
- (as opposed to, say, crashing)?
-* Perf tests: How does the wallet handle withdrawing a *LOT* of coins?
-* Are the transaction history and pending operations reported correctly?
-
-Tests for things the wallet doesn't handle correctly yet:
-
-* What happens if the wallet double-spends a coin?
- (Easy to test by copying the wallet DB before spending
- and then running a spend again with the old DB).
-* What happens when a reserve is changed between accepting withdrawal
- and actually withdrawing coins?
- (This is harder to test. Might not be possible with the current CLI.
- The idea would be be to have some ``--inhibit=withdraw`` flag
- that tells the wallet to not actually withdraw,
- so we can change the reserve state and then resume the wallet.)
-* What happens if the exchange suddenly has a completely new list of denominations on offer?
-* What happens if the exchange changes its master public key?
- The wallet *should* handle this gracefully
- even if we have coins with that exchange,
- provided that the old denominations can be recouped.
- (That one is pretty difficult!)
-* Does the wallet handle :ref:`payment aborts <order-abort>` correctly?
-
-There are test cases that require us to modify the communication between the wallet and exchange.
-
-* What does the wallet do when the exchange/merchant announce an incompatible protocol version?
-* What happens if some signature made by the exchange/merchant is garbage?
-* What if the exchange reports a double-spend and the proof it gives us is invalid?
-
-
Integration Test and Fault Injection Framework
----------------------------------------------
@@ -553,7 +459,7 @@ or errors) are handled properly. (Support for auditors is still pending
but needed to fully test the wallet.)
This is how a simple withdrawal and payment test case looks like:
-`<https://git.taler.net/wallet-core.git/tree/packages/taler-integrationtests/src/test-payment.ts>`__
+`<https://git.taler.net/taler-typescript-core.git/tree/packages/taler-harness/src/integrationtests/test-payment.ts>`__
(What's particularly nice is that all our docs contain TypeScript
definitions for all API request bodies. So just copying them into the
@@ -570,7 +476,7 @@ The following test case (a) logs all requests and responses to the test
harness stdout and (b) at a certain point, starts dropping the next 10
requests to the exchange (testing the wallet's retry logic):
-`<https://git.taler.net/wallet-core.git/tree/packages/taler-integrationtests/src/test-payment-fault.ts#n165>`__
+`<https://git.taler.net/taler-typescript-core.git/tree/packages/taler-harness/src/integrationtests/test-payment-fault.ts#n165>`__
3. All util functionality from JS wallet-core, such as the Taler crypto,
amount/date/etc. handling and JSON parsing/validation (the wallet is now
@@ -642,15 +548,43 @@ which implements the ``ExchangeServiceInterface``:
The package for the integration tests is here:
-`<https://git.taler.net/wallet-core.git/tree/packages/taler-integrationtests>`__
+`<https://git.taler.net/wallet-core.git/tree/packages/taler-harness>`__
-The shortcut to run all integration tests is
+The integration tests are run via the ``taler-harness`` tool.
.. code:: sh
- ./bootstrap && ./configure --prefix=... \
- && make install integrationtests
+ ./bootstrap && ./configure --prefix=... && make install
+ taler-harness run-integrationtests
+
+
+Dev Experiments
+===============
+
+Dev experiments allow simulating certain scenarios that are difficult to
+reproduce otherwise. This allows more comprehensive (manual) testing of the
+UIs.
+
+You can enable dev experiments by putting the wallet into dev mode and then
+scanning the QR code for a ``taler://dev-experiment`` URI that specifies the
+desired dev experiment.
+
+Faking Protocol Versions
+------------------------
+
+The ``start-fakeprotover`` dev experiment can be used to fake the protocol
+version reported by Taler components. It mocks the ``version`` field in the
+response to ``/config`` or ``/keys``.
+
+Usage:
+
+.. code:: none
+
+ taler://dev-experiment/start-fakeprotover?base_url=...&fake_ver=...
+
+Example:
+
+.. code:: none
-(From the root of the whole repo. If you're developing tests, it's way
-faster to just run "make compile install" once and then use
-"./testrunner" from the ``taler-integrationtests`` package.)
+ # Fake version 10:0:0 for https://exchange.demo.taler.net/
+ taler://dev-experiment/start-fakeprotover?base_url=https%3A%2F%2Fexchange.demo.taler.net%2F&fake_ver=10%3A0%3A0