gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-docs] branch master updated: repurchase


From: gnunet
Subject: [taler-docs] branch master updated: repurchase
Date: Wed, 15 Apr 2020 23:30:49 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository docs.

The following commit(s) were added to refs/heads/master by this push:
     new 2e8de1b  repurchase
     new d1ffd25  Merge branch 'master' of git+ssh://git.taler.net/docs
2e8de1b is described below

commit 2e8de1b7de3130d6554d86aa449588f8a49c4379
Author: Christian Grothoff <address@hidden>
AuthorDate: Wed Apr 15 23:30:37 2020 +0200

    repurchase
---
 core/api-merchant.rst           | 2204 ++++++++++++++++++++-------------------
 taler-merchant-api-tutorial.rst |   51 +-
 2 files changed, 1171 insertions(+), 1084 deletions(-)

diff --git a/core/api-merchant.rst b/core/api-merchant.rst
index 0754aa4..7885dd4 100644
--- a/core/api-merchant.rst
+++ b/core/api-merchant.rst
@@ -26,7 +26,8 @@ Merchant Backend API
 WARNING: This document describes the version 1 of the merchant backend
 API, which is NOT yet implemented at all!
 
-TODO: #5210 is not addressed by this specification!
+TODO: https://bugs.gnunet.org/view.php?id=5987#c15127
+      is not yet addressed by this specification!
 
 The ``*/public/*`` endpoints are publicly exposed on the Internet and accessed
 both by the user's browser and their wallet.
@@ -62,21 +63,43 @@ Getting the configuration
       // Currency supported by this backend.
       currency: string;
 
-      // optional array with information about the instances running at this 
backend
-      // FIXME: remove, use/provide http:get:: /instances instead!
-      instances: InstanceInformation[];
     }
 
-  .. ts:def:: InstanceInformation
 
-    interface InstanceInformation {
+--------------------------
+Dynamic Merchant Instances
+--------------------------
+
+.. _instances:
+.. http:get:: /instances
+
+  This is used to return the list of all the merchant instances
+
+  **Response:**
+
+  :status 200 OK:
+    The backend has successfully returned the list of instances stored. Returns
+    a `InstancesResponse`.
+
+  .. ts:def:: InstancesResponse
+
+    interface InstancesResponse {
+      // List of instances that are present in the backend (see `Instance`)
+      instances: Instance[];
+    }
 
-      // Human-readable legal business name served by this instance
+  The `Instance` object describes the instance registered with the backend.
+  It does not include the full details, only those that usually concern the 
frontend.
+  It has the following structure:
+
+  .. ts:def:: Instance
+
+    interface Instance {
+      // Merchant name corresponding to this instance.
       name: string;
 
-      // Base URL of the instance. Can be of the form "/PizzaShop/" or
-      // a fully qualified URL (i.e. "https://backend.example.com/PizzaShop/";).
-      instance_baseurl: string;
+      // Merchant instance of the response to create
+      instance: string;
 
       // Public key of the merchant/instance, in Crockford Base32 encoding.
       merchant_pub: EddsaPublicKey;
@@ -86,1621 +109,1649 @@ Getting the configuration
       // front-ends do not have to support wallets selecting payment targets.
       payment_targets: string[];
 
-      // Base URL of the exchange this instance uses for tipping.
-      // Optional, only present if the instance supports tipping.
-      // FIXME: obsolete with current tipping API!
-      tipping_exchange_baseurl?: string;
+   }
 
-    }
 
+.. http:post:: /instances
 
-------------------
-Receiving Payments
-------------------
+  This request will be used to create a new merchant instance in the backend.
 
-.. _post-order:
+  **Request:**
 
-.. http:post:: /orders
+  The request must be a `InstanceConfigurationMessage`.
 
-  Create a new order that a customer can pay for.
+  **Response:**
 
-  This request is **not** idempotent unless an ``order_id`` is explicitly 
specified.
-  However, while repeating without an ``order_id`` will create another order, 
that is
-  generally pretty harmless (as long as only one of the orders is returned to 
the wallet).
+  :status 204 No content:
+    The backend has successfully created the instance.
+  :status 409 Conflict:
+    This instance already exists, but with other configuration options.
+    Use "PATCH" to update an instance configuration.
 
-  .. note::
+  .. ts:def:: InstanceConfigurationMessage
 
-    This endpoint does not return a URL to redirect your user to confirm the
-    payment.  In order to get this URL use :http:get:`/orders/$ORDER_ID`.  The
-    API is structured this way since the payment redirect URL is not unique
-    for every order, there might be varying parameters such as the session id.
+    interface InstanceConfigurationMessage {
+      // The URI where the wallet will send coins.  A merchant may have
+      // multiple accounts, thus this is an array.  Note that by
+      // removing URIs from this list
+      payto_uris: string[];
 
-  **Request:**
+      // Name of the merchant instance to create (will become $INSTANCE).
+      instance: string;
 
-  The request must be a `PostOrderRequest`.
+      // Merchant name corresponding to this instance.
+      name: string;
 
-  **Response:**
+      // The merchant's physical address (to be put into contracts).
+      address: Location;
 
-  :status 200 OK:
-    The backend has successfully created the proposal.  The response is a
-    :ts:type:`PostOrderResponse`.
-  :status 404 Not found:
-    The order given used products from the inventory, but those were not found
-    in the inventory.  Or the merchant instance is unknown.  Details in the
-    error code. NOTE: no good way to find out which product is not in the
-    inventory, we MAY want to specify that in the reply.
-  :status 410 Gone:
-    The order given used products from the inventory that are out of stock.
-    The reponse is a :ts:type:`OutOfStockResponse`.
+      // The jurisdiction under which the merchant conducts its business
+      // (to be put into contracts).
+      jurisdiction: Location;
+    }
 
 
-  .. ts:def:: PostOrderRequest
+.. http:patch:: /instances/$INSTANCE
 
-    interface PostOrderRequest {
-      // The order must at least contain the minimal
-      // order detail, but can override all
-      order: Order;
+  Update the configuration of a merchant instance.
 
-      // specifies the payment target preferred by the client. Can be used
-      // to select among the various (active) wire methods supported by the 
instance.
-      payment_target: string;
-
-      // specifies that inventory management is desired.  If not given,
-      // the backend does NOT check for the availability of stocks and
-      // ignores the ``product_id`` (except to possibly fill in details
-      // about the product, if ``auto_complete`` is requested).
-      manage_inventory: boolean;
-
-      // specifies that automatically completing fields based on the
-      // inventory data is desired.  If given,
-      // the backend tries to expand the products-list with additional
-      // information and -- if missing -- will also compute the
-      // total amount (as the sum of the product of price times
-      // quantity for all items in the order).  Frontends can still override
-      // individual product prices or the total simply by providing them,
-      // for example to provide discounts.
-      auto_complete: boolean;
+  **Request**
 
-      // Specifies a lock identifier that was used to
-      // lock a product in the inventory.  Only useful if
-      // ``manage_inventory`` is set.  Used in case a frontend
-      // reserved quantities of the individual products while
-      // the shopping card was being built.  Multiple UUIDs can
-      // be used in case different UUIDs were used for different
-      // products (i.e. in case the user started with multiple
-      // shopping sessions that were combined during checkout).
-      lock_uuids?: UUID[];
+  The request must be a `InstanceUpdateMessage`.  Fields that are not
+  provided are not modified.  Removing an existing payto_uri deactivates
+  the account (it will no longer be used for future contracts).
 
-    }
+  **Response:**
 
-    type Order : MinimalOrderDetail | InventoryOrderDetail | ContractTerms;
+  :status 204 No content:
+    The backend has successfully created the instance.
+  :status 404 Not found:
+    This instance is unknown and thus cannot be reconfigured.
 
-  The following fields must be specified in the ``order`` field of the 
request.  Other fields from
-  `ContractTerms` are optional, and will override the defaults in the merchant 
configuration.
+  .. ts:def:: InstanceUpdateMessage
 
-  .. ts:def:: MinimalOrderDetail
+    interface InstanceUpdateMessage {
+      // The URI where the wallet will send coins.  A merchant may have
+      // multiple accounts, thus this is an array.  Note that by
+      // removing URIs from this list
+      payto_uris?: string[];
 
-    interface MinimalOrderDetail {
-      // Amount to be paid by the customer
-      amount: Amount;
+      // Merchant name corresponding to this instance.
+      name?: string;
 
-      // Short summary of the order
-      summary: string;
+      // The merchant's physical address (to be put into contracts).
+      address?: Location;
 
-      // URL that will show that the order was successful after
-      // it has been paid for.  The wallet must always automatically append
-      // the order_id as a query parameter to this URL when using it.
-      fulfillment_url: string;
+      // The jurisdiction under which the merchant conducts its business
+      // (to be put into contracts).
+      jurisdiction?: Location;
     }
 
-  The following fields can be specified if the order is inventory-based.
-  In this case, the backend can compute the amounts from the prices given
-  in the inventory.  Note that if the frontend does give more details
-  (towards the ContractTerms), this will override those details
-  (including total price) that would otherwise computed based on information
-  from the inventory.
+.. http:get:: /instances/$INSTANCE
+
+  This is used to query a specific merchant instance.
 
-  .. ts:def:: InventoryOrderDetail
+  **Response:**
 
-    interface InventoryOrderDetail {
+  :status 200 OK:
+    The backend has successfully returned the list of instances stored. Returns
+    a `QueryInstancesResponse`.
 
-      // List of products that are part of the purchase (see `Product`),
-      // possibly incomplete as details can be filled from the inventory 
detail.
-      products: ProductSpecification[];
-    }
+  .. ts:def:: QueryInstancesResponse
 
-    type ProductSpecification : (MinimalInventoryProduct | Product);
+    interface QueryInstancesResponse {
+      // The URI where the wallet will send coins.  A merchant may have
+      // multiple accounts, thus this is an array.
+      accounts: MerchantAccount[];
 
+      // Merchant name corresponding to this instance.
+      name: string;
 
-  .. ts:def:: MinimalInventoryProduct
+      // Public key of the merchant/instance, in Crockford Base32 encoding.
+      merchant_pub: EddsaPublicKey;
 
-    Note that if the frontend does give details beyond these,
-    it will override those details (including price or taxes)
-    that the backend would otherwise fill in via the inventory.
+      // The merchant's physical address (to be put into contracts).
+      address: Location;
 
-    interface MinimalInventoryProduct {
-      // Which product is requested (here mandatory!)
-      product_id: string;
+      // The jurisdiction under which the merchant conducts its business
+      // (to be put into contracts).
+      jurisdiction: Location;
 
-      // How many units of the product are requested
-      quantity: integer;
     }
 
+  .. ts:def:: MerchantAccount
 
-  .. ts:def:: PostOrderResponse
+    interface MerchantAccount {
 
-    interface PostOrderResponse {
-      // Order ID of the response that was just created
-      order_id: string;
-    }
+      // payto:// URI of the account.
+      payto_uri: string;
 
+      // Hash over the wire details (including over the salt)
+      h_wire: HashCode;
 
-  .. ts:def:: OutOfStockResponse
+      // salt used to compute h_wire
+      salt: string;
 
-    interface OutOfStockResponse {
-      // Which items are out of stock?
-      missing_products: OutOfStockEntry;
+      // true if this account is active,
+      // false if it is historic.
+      active: boolean;
     }
 
-    interface OutOfStockEntry {
-      // Product ID of an out-of-stock item
-      product_id: string;
 
-      // Requested quantity
-      requested_quantity: integer;
 
-      // Available quantity (must be below ``requested_quanitity``)
-      available_quantity: integer;
+.. http:delete:: /instances/$INSTANCE
 
-      // When do we expect the product to be again in stock?
-      // Optional, not given if unknown.
-      restock_expected?: timestamp;
-    }
+  This request will be used to delete (permanently disable)
+  or purge merchant instance in the backend. Purging will
+  delete all offers and payments associated with the instance,
+  while disabling (the default) only deletes the private key
+  and makes the instance unusuable for new orders or payments.
 
+  **Request:**
 
+  :query purge: *Optional*. If set to YES, the instance will be fully
+      deleted. Otherwise only the private key would be deleted.
 
-.. http:get:: /orders
+  **Response**
 
-  Returns known orders up to some point in the past.
+  :status 204 NoContent:
+    The backend has successfully removed the instance.  The response is a
+    `PostInstanceRemoveResponse`.
+  :status 404 Not found:
+    The instance is unknown to the backend.
+  :status 409 Conflict:
+    The instance cannot be deleted because it has pending offers, or
+    the instance cannot be purged because it has successfully processed
+    payments that have not passed the TAX_RECORD_EXPIRATION time.
+    The latter case only applies if ``purge`` was set.
 
-  **Request:**
 
-  :query paid: *Optional*. If set to yes, only return paid orders, if no only 
unpaid orders. Do not give (or use "all") to see all orders regardless of 
payment status.
-  :query aborted: *Optional*. If set to yes, only return aborted orders, if no 
only unaborted orders. Do not give (or use "all")  to see all orders regardless 
of abort status.
-  :query refunded: *Optional*. If set to yes, only return refunded orders, if 
no only unrefunded orders. Do not give (or use "all") to see all orders 
regardless of refund status.
-  :query wired: *Optional*. If set to yes, only return wired orders, if no 
only orders with missing wire transfers. Do not give (or use "all") to see all 
orders regardless of wire transfer status.
-  :query date: *Optional.* Time threshold, see ``delta`` for its 
interpretation.  Defaults to the oldest or most recent entry, depending on 
``delta``.
-  :query start: *Optional*. Row number threshold, see ``delta`` for its 
interpretation.  Defaults to ``UINT64_MAX``, namely the biggest row id possible 
in the database.
-  :query delta: *Optional*. takes value of the form ``N (-N)``, so that at 
most ``N`` values strictly younger (older) than ``start`` and ``date`` are 
returned.  Defaults to ``-20``.
-  :query timeout_ms: *Optional*. Timeout in milli-seconds to wait for 
additional orders if the answer would otherwise be negative (long polling). 
Only useful if delta is positive. Note that the merchant MAY still return a 
response that contains fewer than delta orders.
+--------------------
+Inventory management
+--------------------
 
-  **Response:**
+.. _inventory:
 
-  :status 200 OK:
-    The response is an `OrderHistory`.
+Inventory management is an *optional* backend feature that can be used to
+manage limited stocks of products and to auto-complete product descriptions in
+contracts (such that the frontends have to do less work).  You can use the
+Taler merchant backend to process payments *without* using its inventory
+management.
 
-  .. ts:def:: OrderHistory
 
-    interface OrderHistory {
-      // timestamp-sorted array of all orders matching the query.
-      // The order of the sorting depends on the sign of ``delta``.
-      orders : OrderHistory[];
-    }
+.. http:get:: /products
 
+  This is used to return the list of all items in the inventory.
 
-  .. ts:def:: OrderHistoryEntry
+  **Response:**
 
-    interface OrderHistoryEntry {
-      // The serial number this entry has in the merchant's DB.
-      row_id: number;
+  :status 200 OK:
+    The backend has successfully returned the inventory. Returns
+    a `InventorySummaryResponse`.
 
-      // order ID of the transaction related to this entry.
-      order_id: string;
+  .. ts:def:: InventorySummaryResponse
 
-      // Transaction's timestamp
-      timestamp: Timestamp;
+    interface InventorySummaryResponse {
+      // List of items that are present in the inventory
+      items: InventoryEntry[];
+    }
 
-      // Total amount the customer should pay for this order.
-      total: Amount;
+  The `InventoryEntry` object describes an item in the inventory. It has the 
following structure:
 
-      // Total amount the customer did pay for this order.
-      paid: Amount;
+  .. ts:def:: InventoryEntry
 
-      // Total amount the customer was refunded for this order.
-      // (includes abort-refund and refunds, boolean flag
-      // below can help determine which case it is).
-      refunded: Amount;
+    interface InventoryEntry {
+      // Product identifier, as found in the product.
+      product_id: string;
 
-      // Was the order ever fully paid?
-      is_paid: boolean;
+      // Amount of the product in stock. Given in product-specific units.
+      // Set to -1 for "infinite" (i.e. for "electronic" books).
+      stock: integer;
 
+      // unit in which the product is metered (liters, kilograms, packages, 
etc.)
+      unit: string;
     }
 
 
+.. http:get:: /products/$PRODUCT_ID
 
+  This is used to obtain detailed information about a product in the inventory.
 
-.. http:post:: /public/orders/$ORDER_ID/claim
+  **Response:**
 
-  Wallet claims ownership (via nonce) over an order.  By claiming
-  an order, the wallet obtains the full contract terms, and thereby
-  implicitly also the hash of the contract terms it needs for the
-  other ``/public/`` APIs to authenticate itself as the wallet that
-  is indeed eligible to inspect this particular order's status.
+  :status 200 OK:
+    The backend has successfully returned the inventory. Returns
+    a `ProductDetail`.
 
-  **Request:**
+  .. ts:def:: ProductDetail
 
-  The request must be a `ClaimRequest`
-
-  .. ts:def:: ClaimRequest
-
-    interface ClaimRequest {
-      // Nonce to identify the wallet that claimed the order.
-      nonce: string;
-    }
+    interface ProductDetail {
 
-  **Response:**
+      // Human-readable product description.
+      description: string;
 
-  :status 200 OK:
-    The client has successfully claimed the order.
-    The response contains the :ref:`contract terms <ContractTerms>`.
-  :status 404 Not found:
-    The backend is unaware of the instance or order.
-  :status 409 Conflict:
-    The someone else claimed the same order ID with different nonce before.
+      // Map from IETF BCP 47 language tags to localized descriptions
+      description_i18n?: { [lang_tag: string]: string };
 
+      // unit in which the product is measured (liters, kilograms, packages, 
etc.)
+      unit: string;
 
-.. http:post:: /public/orders/$ORDER_ID/pay
+      // The price for one ``unit`` of the product. Zero is used
+      // to imply that this product is not sold separately, or
+      // that the price is not fixed, and must be supplied by the
+      // front-end.  If non-zero, this price MUST include applicable
+      // taxes.
+      price: Amount;
 
-  Pay for an order by giving a deposit permission for coins.  Typically used by
-  the customer's wallet.  Note that this request does not include the
-  usual ``h_contract`` argument to authenticate the wallet, as the hash of
-  the contract is implied by the signatures of the coins.  Furthermore, this
-  API doesn't really return useful information about the order.
+      // An optional base64-encoded product image
+      image?: ImageDataUrl;
 
-  **Request:**
+      // a list of taxes paid by the merchant for one unit of this product
+      taxes: Tax[];
 
-  The request must be a `pay request <PayRequest>`.
+      // Number of units of the product in stock in sum in total,
+      // including all existing sales ever. Given in product-specific
+      // units.
+      // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+      total_stocked: integer;
 
-  **Response:**
+      // Number of units of the product that have already been sold.
+      total_sold: integer;
 
-  :status 200 OK:
-    The exchange accepted all of the coins.
-    The body is a `payment response <PaymentResponse>`.
-    The ``frontend`` should now fullfill the contract.
-  :status 400 Bad request:
-    Either the client request is malformed or some specific processing error
-    happened that may be the fault of the client as detailed in the JSON body
-    of the response.
-  :status 403 Forbidden:
-    One of the coin signatures was not valid.
-  :status 404 Not found:
-    The merchant backend could not find the order or the instance
-    and thus cannot process the payment.
-  :status 409 Conflict:
-    The exchange rejected the payment because a coin was already spent before.
-    The response will include the ``coin_pub`` for which the payment failed,
-    in addition to the response from the exchange to the ``/deposit`` request.
-  :status 412 Precondition Failed:
-    The given exchange is not acceptable for this merchant, as it is not in the
-    list of accepted exchanges and not audited by an approved auditor.
-  :status 424 Failed Dependency:
-    The merchant's interaction with the exchange failed in some way.
-    The client might want to try later again.
-    This includes failures like the denomination key of a coin not being
-    known to the exchange as far as the merchant can tell.
+      // Number of units of the product that were lost (spoiled, stolen, etc.)
+      total_lost: integer;
 
-  The backend will return verbatim the error codes received from the exchange's
-  :ref:`deposit <deposit>` API.  If the wallet made a mistake, like by
-  double-spending for example, the frontend should pass the reply verbatim to
-  the browser/wallet.  If the payment was successful, the frontend MAY use
-  this to trigger some business logic.
+      // Number of units of the product that are currently locked by some
+      // shopping cart.
+      total_locked: integer;
 
-  .. ts:def:: PaymentResponse
+      // Identifies where the product is in stock.
+      location?: Location;
 
-    interface PaymentResponse {
-      // Signature on ``TALER_PaymentResponsePS`` with the public
-      // key of the merchant instance.
-      sig: EddsaSignature;
+      // Identifies when we expect the next restocking to happen.
+      next_restock?: timestamp;
 
     }
 
-  .. ts:def:: PayRequest
 
-    interface PayRequest {
-      coins: CoinPaySig[];
-    }
+.. http:post:: /products
 
-  .. ts:def:: CoinPaySig
+  This is used to add a product to the inventory.
 
-    export interface CoinPaySig {
-      // Signature by the coin.
-      coin_sig: string;
+  **Request:**
 
-      // Public key of the coin being spend.
-      coin_pub: string;
+  The request must be a `ProductAddDetail`.
 
-      // Signature made by the denomination public key.
-      ub_sig: string;
+  **Response:**
 
-      // The denomination public key associated with this coin.
-      denom_pub: string;
+  :status 204 No content:
+    The backend has successfully expanded the inventory.
+  :status 409 Conflict:
+    The backend already knows a product with this product ID, but with 
different details.
 
-      // The amount that is subtracted from this coin with this payment.
-      contribution: Amount;
 
-      // URL of the exchange this coin was withdrawn from.
-      exchange_url: string;
-    }
+  .. ts:def:: ProductAddDetail
 
+    interface ProductAddDetail {
 
-.. http:post:: /public/orders/$ORDER_ID/abort
+      // product ID to use.
+      product_id: string;
 
-  Abort paying for an order and obtain a refund for coins that
-  were already deposited as part of a failed payment.
+      // Human-readable product description.
+      description: string;
 
-  **Request:**
+      // Map from IETF BCP 47 language tags to localized descriptions
+      description_i18n?: { [lang_tag: string]: string };
 
-  The request must be an `abort request <AbortRequest>`.
+      // unit in which the product is measured (liters, kilograms, packages, 
etc.)
+      unit: string;
 
-  :query h_contract: hash of the order's contract terms (this is used to 
authenticate the wallet/customer in case $ORDER_ID is guessable). *Mandatory!*
+      // The price for one ``unit`` of the product. Zero is used
+      // to imply that this product is not sold separately, or
+      // that the price is not fixed, and must be supplied by the
+      // front-end.  If non-zero, this price MUST include applicable
+      // taxes.
+      price: Amount;
 
-  **Response:**
+      // An optional base64-encoded product image
+      image?: ImageDataUrl;
 
-  :status 200 OK:
-    The exchange accepted all of the coins. The body is a
-    a `merchant refund response <MerchantRefundResponse>`.
-  :status 400 Bad request:
-    Either the client request is malformed or some specific processing error
-    happened that may be the fault of the client as detailed in the JSON body
-    of the response.
-  :status 403 Forbidden:
-    The ``h_contract`` does not match the order.
-  :status 404 Not found:
-    The merchant backend could not find the order or the instance
-    and thus cannot process the abort request.
-  :status 412 Precondition Failed:
-    Aborting the payment is not allowed, as the original payment did succeed.
-  :status 424 Failed Dependency:
-    The merchant's interaction with the exchange failed in some way.
-    The error from the exchange is included.
+      // a list of taxes paid by the merchant for one unit of this product
+      taxes: Tax[];
 
-  The backend will return verbatim the error codes received from the exchange's
-  :ref:`refund <refund>` API.  The frontend should pass the replies verbatim to
-  the browser/wallet.
+      // Number of units of the product in stock in sum in total,
+      // including all existing sales ever. Given in product-specific
+      // units.
+      // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+      total_stocked: integer;
 
-  .. ts:def:: AbortRequest
+      // Identifies where the product is in stock.
+      location?: Location;
 
-    interface AbortRequest {
-      // List of coins the wallet would like to see refunds for.
-      // (Should be limited to the coins for which the original
-      // payment succeeded, as far as the wallet knows.)
-      coins: AbortedCoin[];
-    }
+      // Identifies when we expect the next restocking to happen.
+      next_restock?: timestamp;
 
-    interface AbortedCoin {
-      // Public key of a coin for which the wallet is requesting an 
abort-related refund.
-      coin_pub: EddsaPublicKey;
     }
 
 
 
-.. http:get:: /orders/$ORDER_ID/
+.. http:patch:: /products/$PRODUCT_ID
 
-  Merchant checks the payment status of an order.  If the order exists but is 
not payed
-  yet, the response provides a redirect URL.  When the user goes to this URL,
-  they will be prompted for payment.  Differs from the ``/public/`` API both
-  in terms of what information is returned and in that the wallet must provide
-  the contract hash to authenticate, while for this API we assume that the
-  merchant is authenticated (as the endpoint is not ``/public/``).
+  This is used to update product details in the inventory. Note that the
+  ``total_stocked`` and ``total_lost`` numbers MUST be greater or equal than
+  previous values (this design ensures idempotency).  In case stocks were lost
+  but not sold, increment the ``total_lost`` number.  All fields in the
+  request are optional, those that are not given are simply preserved (not
+  modified).  Note that the ``description_i18n`` and ``taxes`` can only be
+  modified in bulk: if it is given, all translations must be provided, not
+  only those that changed.  "never" should be used for the ``next_restock``
+  timestamp to indicate no intention/possibility of restocking, while a time
+  of zero is used to indicate "unknown".
+
+  Limitations: you cannot remove a ``location`` from a product that used to
+  have a location.
 
   **Request:**
 
-  :query session_id: *Optional*. Session ID that the payment must be bound to. 
 If not specified, the payment is not session-bound.
-  :query transfer: *Optional*. If set to "YES", try to obtain the wire 
transfer status for this order from the exchange. Otherwise, the wire transfer 
status MAY be returned if it is available.
-  :query timeout_ms: *Optional*. Timeout in milli-seconds to wait for a 
payment if the answer would otherwise be negative (long polling).
+  The request must be a `ProductPatchDetail`.
 
   **Response:**
 
-  :status 200 OK:
-    Returns a `MerchantOrderStatusResponse`, whose format can differ based on 
the status of the payment.
-  :status 404 Not Found:
-    The order or instance is unknown to the backend.
-  :status 409 Conflict:
-    The exchange previously claimed that a deposit was not included in a wire
-    transfer, and now claims that it is.  This means that the exchange is
-    dishonest.  The response contains the cryptographic proof that the exchange
-    is misbehaving in the form of a `TransactionConflictProof`.
-  :status 424 Failed dependency:
-    We failed to obtain a response from the exchange about the
-    wire transfer status.
-
-  .. ts:def:: MerchantOrderStatusResponse
-
-    type MerchantOrderStatusResponse = CheckPaymentPaidResponse | 
CheckPaymentUnpaidResponse
-
-  .. ts:def:: CheckPaymentPaidResponse
-
-    interface CheckPaymentPaidResponse {
-      paid: true;
-
-      // Was the payment refunded (even partially)
-      refunded: boolean;
-
-      // Amount that was refunded, only present if refunded is true.
-      refund_amount?: Amount;
+  :status 204 No content:
+    The backend has successfully expanded the inventory.
 
-      // Contract terms
-      contract_terms: ContractTerms;
 
-      // If available, the wire transfer status from the exchange for this 
order
-      wire_details?: TransactionWireTransfer;
-    }
+    interface ProductPatchDetail {
 
-  .. ts:def:: CheckPaymentUnpaidResponse
+      // Human-readable product description.
+      description?: string;
 
-    interface CheckPaymentUnpaidResponse {
-      paid: false;
+      // Map from IETF BCP 47 language tags to localized descriptions
+      description_i18n?: { [lang_tag: string]: string };
 
-      // URI that the wallet must process to complete the payment.
-      taler_pay_uri: string;
+      // unit in which the product is measured (liters, kilograms, packages, 
etc.)
+      unit?: string;
 
-      // Alternative order ID which was paid for already in the same session.
-      // Only given if the same product was purchased before in the same 
session.
-      already_paid_order_id?: string;
+      // The price for one ``unit`` of the product. Zero is used
+      // to imply that this product is not sold separately, or
+      // that the price is not fixed, and must be supplied by the
+      // front-end.  If non-zero, this price MUST include applicable
+      // taxes.
+      price?: Amount;
 
-      // FIXME: why do we NOT return the contract terms here?
-    }
+      // An optional base64-encoded product image
+      image?: ImageDataUrl;
 
-  .. ts:def:: TransactionWireTransfer
+      // a list of taxes paid by the merchant for one unit of this product
+      taxes?: Tax[];
 
-    interface TransactionWireTransfer {
+      // Number of units of the product in stock in sum in total,
+      // including all existing sales ever. Given in product-specific
+      // units.
+      // A value of -1 indicates "infinite" (i.e. for "electronic" books).
+      total_stocked?: integer;
 
-      // Responsible exchange
-      exchange_uri: string;
+      // Number of units of the product that were lost (spoiled, stolen, etc.)
+      total_lost?: integer;
 
-      // 32-byte wire transfer identifier
-      wtid: Base32;
+      // Identifies where the product is in stock.
+      location?: Location;
 
-      // execution time of the wire transfer
-      execution_time: Timestamp;
+      // Identifies when we expect the next restocking to happen.
+      next_restock?: timestamp;
 
-      // Total amount that has been wire transfered
-      // to the merchant
-      amount: Amount;
     }
 
-  .. ts:def:: TransactionConflictProof
-
-    interface TransactionConflictProof {
-      // Numerical `error code <error-codes>`
-      code: number;
-
-      // Human-readable error description
-      hint: string;
-
-      // A claim by the exchange about the transactions associated
-      // with a given wire transfer; it does not list the
-      // transaction that ``transaction_tracking_claim`` says is part
-      // of the aggregate.  This is
-      // a ``/track/transfer`` response from the exchange.
-      wtid_tracking_claim: TrackTransferResponse;
-
-      // The current claim by the exchange that the given
-      // transaction is included in the above WTID.
-      // (A response from ``/track/order``).
-      transaction_tracking_claim: TrackTransactionResponse;
 
-      // Public key of the coin for which we got conflicting information.
-      coin_pub: CoinPublicKey;
-
-    }
 
+.. http:post:: /products/$PRODUCT_ID/lock
 
-.. http:get:: /public/orders/$ORDER_ID/
+  This is used to lock a certain quantity of the product for a limited
+  duration while the customer assembles a complete order.  Note that
+  frontends do not have to "unlock", they may rely on the timeout as
+  given in the ``duration`` field.  Re-posting a lock with a different
+  ``duration`` or ``quantity`` updates the existing lock for the same UUID
+  and does not result in a conflict.
 
-  Query the payment status of an order. This endpoint is for the wallet.
-  When the wallet goes to this URL and it is unpaid,
-  they will be prompted for payment.
+  Unlocking by using a ``quantity`` of zero is is
+  optional but recommended if customers remove products from the
+  shopping cart. Note that actually POSTing to ``/orders`` with set
+  ``manage_inventory`` and using ``lock_uuid`` will **transition** the
+  lock to the newly created order (which may have a different ``duration``
+  and ``quantity`` than what was requested in the lock operation).
+  If an order is for fewer items than originally locked, the difference
+  is automatically unlocked.
 
   **Request:**
 
-  :query h_contract: hash of the order's contract terms (this is used to 
authenticate the wallet/customer in case $ORDER_ID is guessable). *Mandatory!*
-  :query session_id: *Optional*. Session ID that the payment must be bound to. 
 If not specified, the payment is not session-bound.
-  :query timeout_ms: *Optional.*  If specified, the merchant backend will
-    wait up to ``timeout_ms`` milliseconds for completion of the payment before
-    sending the HTTP response.  A client must never rely on this behavior, as 
the
-    merchant backend may return a response immediately.
-  :query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund 
above the given AMOUNT. Only useful in combination with timeout.
+  The request must be a `LockRequest`.
 
   **Response:**
 
-  :status 200 OK:
-    The response is a `PublicPayStatusResponse`, with ``paid`` true.
-    FIXME: what about refunded?
-  :status 402 Payment required:
-    The response is a `PublicPayStatusResponse`, with ``paid`` false.
-    FIXME: what about refunded?
-  :status 403 Forbidden:
-    The ``h_contract`` does not match the order.
+  :status 204 No content:
+    The backend has successfully locked (or unlocked) the requested 
``quantity``.
   :status 404 Not found:
-    The merchant backend is unaware of the order.
-
-  .. ts:def:: PublicPayStatusResponse
-
-    interface PublicPayStatusResponse {
-      // Has the payment for this order (ever) been completed?
-      paid: boolean;
+    The backend has does not know this product.
+  :status 410 Gone:
+    The backend does not have enough of product in stock.
 
-      // Was the payment refunded (even partially, via refund or abort)?
-      refunded: boolean;
+  .. ts:def:: LockRequest
 
-      // Amount that was refunded in total.
-      refund_amount: Amount;
+    interface LockRequest {
 
-      // Refunds for this payment, empty array for none.
-      refunds: RefundDetail[];
+      // UUID that identifies the frontend performing the lock
+      lock_uuid: UUID;
 
-      // URI that the wallet must process to complete the payment.
-      taler_pay_uri: string;
+      // How long does the frontend intend to hold the lock
+      duration: time;
 
-      // Alternative order ID which was paid for already in the same session.
-      // Only given if the same product was purchased before in the same 
session.
-      already_paid_order_id?: string;
+      // How many units should be locked?
+      quantity: integer;
 
     }
 
 
-.. http:delete:: /orders/$ORDER_ID
 
-  Delete information about an order.  Fails if the order was paid in the
-  last 10 years (or whatever TAX_RECORD_EXPIRATION is set to) or was
-  claimed but is unpaid and thus still a valid offer.
 
-  **Response:**
+------------------
+Receiving Payments
+------------------
 
-  :status 204 No content:
-    The backend has successfully deleted the order.
-  :status 404 Not found:
-    The backend does not know the instance or the order.
-  :status 409 Conflict:
-    The backend refuses to delete the order.
+.. _post-order:
 
+.. http:post:: /orders
 
---------------
-Giving Refunds
---------------
+  Create a new order that a customer can pay for.
 
+  This request is **not** idempotent unless an ``order_id`` is explicitly 
specified.
+  However, while repeating without an ``order_id`` will create another order, 
that is
+  generally pretty harmless (as long as only one of the orders is returned to 
the wallet).
 
-.. http:post:: /orders/$ORDER_ID/refund
+  .. note::
 
-  Increase the refund amount associated with a given order.  The user should be
-  redirected to the ``taler_refund_url`` to trigger refund processing in the 
wallet.
+    This endpoint does not return a URL to redirect your user to confirm the
+    payment.  In order to get this URL use :http:get:`/orders/$ORDER_ID`.  The
+    API is structured this way since the payment redirect URL is not unique
+    for every order, there might be varying parameters such as the session id.
 
   **Request:**
 
-  The request body is a `RefundRequest` object.
+  The request must be a `PostOrderRequest`.
 
   **Response:**
 
   :status 200 OK:
-    The refund amount has been increased, the backend responds with a 
`MerchantRefundResponse`
+    The backend has successfully created the proposal.  The response is a
+    :ts:type:`PostOrderResponse`.
   :status 404 Not found:
-    The order is unknown to the merchant
-  :status 409 Conflict:
-    The refund amount exceeds the amount originally paid
+    The order given used products from the inventory, but those were not found
+    in the inventory.  Or the merchant instance is unknown.  Details in the
+    error code. NOTE: no good way to find out which product is not in the
+    inventory, we MAY want to specify that in the reply.
+  :status 410 Gone:
+    The order given used products from the inventory that are out of stock.
+    The reponse is a :ts:type:`OutOfStockResponse`.
 
-  .. ts:def:: RefundRequest
 
-    interface RefundRequest {
-      // Amount to be refunded
-      refund: Amount;
+  .. ts:def:: PostOrderRequest
 
-      // Human-readable refund justification
-      reason: string;
-    }
+    interface PostOrderRequest {
+      // The order must at least contain the minimal
+      // order detail, but can override all
+      order: Order;
 
-  .. ts:def:: MerchantRefundResponse
+      // specifies the payment target preferred by the client. Can be used
+      // to select among the various (active) wire methods supported by the 
instance.
+      payment_target?: string;
 
-    interface MerchantRefundResponse {
+      // specifies that some products are to be included in the
+      // order from the inventory.  For these inventory management
+      // is performed (so the products must be in stock) and
+      // details are completed from the product data of the backend.
+      inventory_products?: MinimalInventoryProduct[];
 
-      // Hash of the contract terms of the contract that is being refunded.
-      // FIXME: why do we return this?
-      h_contract_terms: HashCode;
+      // Specifies a lock identifier that was used to
+      // lock a product in the inventory.  Only useful if
+      // ``manage_inventory`` is set.  Used in case a frontend
+      // reserved quantities of the individual products while
+      // the shopping card was being built.  Multiple UUIDs can
+      // be used in case different UUIDs were used for different
+      // products (i.e. in case the user started with multiple
+      // shopping sessions that were combined during checkout).
+      lock_uuids?: UUID[];
 
-      // URL (handled by the backend) that the wallet should access to
-      // trigger refund processing.
-      // FIXME: isn't this basically now always ``/public/orders/$ORDER_ID/``?
-      // If so, why return this?
-      taler_refund_url: string;
     }
 
+    type Order : MinimalOrderDetail | ContractTerms;
 
+  The following fields must be specified in the ``order`` field of the 
request.  Other fields from
+  `ContractTerms` are optional, and will override the defaults in the merchant 
configuration.
 
-------------------------
-Tracking Wire Transfers
-------------------------
-
-.. http:post:: /transfers
+  .. ts:def:: MinimalOrderDetail
 
-  Inform the backend over an incoming wire transfer. The backend should 
inquire about the details with the exchange and mark the respective orders as 
wired.  Note that the request will fail if the WTID is not unique (which should 
be guaranteed by a correct exchange).
+    interface MinimalOrderDetail {
+      // Amount to be paid by the customer
+      amount: Amount;
 
-  **Request:**
+      // Short summary of the order
+      summary: string;
 
-   The request must provide `transfer information <TransferInformation>`.
+      // URL that will show that the order was successful after
+      // it has been paid for.  The wallet must always automatically append
+      // the order_id as a query parameter to this URL when using it.
+      fulfillment_url: string;
+    }
 
-  **Response:**
+  The following fields can be specified if the order is inventory-based.
+  In this case, the backend can compute the amounts from the prices given
+  in the inventory.  Note that if the frontend does give more details
+  (towards the ContractTerms), this will override those details
+  (including total price) that would otherwise computed based on information
+  from the inventory.
 
-  :status 200 OK:
-    The wire transfer is known to the exchange, details about it follow in the 
body.
-    The body of the response is a `TrackTransferResponse`.  Note that
-    the similarity to the response given by the exchange for a /track/transfer
-    is completely intended.
+    type ProductSpecification : (MinimalInventoryProduct | Product);
 
-  :status 404 Not Found:
-    The wire transfer identifier is unknown to the exchange.
 
-  :status 409 Conflict:
-    The wire transfer identifier is already known to us, but for a different 
amount,
-    wire method or exchange.
+  .. ts:def:: MinimalInventoryProduct
 
-  :status 424 Failed Dependency: The exchange provided conflicting information 
about the transfer. Namely,
-    there is at least one deposit among the deposits aggregated by ``wtid`` 
that accounts for a coin whose
-    details don't match the details stored in merchant's database about the 
same keyed coin.
-    The response body contains the `TrackTransferConflictDetails`.
+    Note that if the frontend does give details beyond these,
+    it will override those details (including price or taxes)
+    that the backend would otherwise fill in via the inventory.
 
-  .. ts:def:: TransferInformation
+    interface MinimalInventoryProduct {
+      // Which product is requested (here mandatory!)
+      product_id: string;
 
-    interface TransferInformation {
-      // how much was wired to the merchant (minus fees)
-      credit_amount: Amount;
+      // How many units of the product are requested
+      quantity: integer;
+    }
 
-      // raw wire transfer identifier identifying the wire transfer (a 
base32-encoded value)
-      wtid: FIXME;
 
-      // name of the wire transfer method used for the wire transfer
-      // FIXME: why not a payto URI?
-      wire_method;
+  .. ts:def:: PostOrderResponse
 
-      // base URL of the exchange that made the wire transfer
-      exchange: string;
+    interface PostOrderResponse {
+      // Order ID of the response that was just created
+      order_id: string;
     }
 
-  .. ts:def:: TrackTransferResponse
 
-    interface TrackTransferResponse {
-      // Total amount transferred
-      total: Amount;
+  .. ts:def:: OutOfStockResponse
 
-      // Applicable wire fee that was charged
-      wire_fee: Amount;
+    interface OutOfStockResponse {
+      // Which items are out of stock?
+      missing_products: OutOfStockEntry;
+    }
 
-      // public key of the merchant (identical for all deposits)
-      // FIXME: why return this?
-      merchant_pub: EddsaPublicKey;
+    interface OutOfStockEntry {
+      // Product ID of an out-of-stock item
+      product_id: string;
 
-      // hash of the wire details (identical for all deposits)
-      // FIXME: why return this? Isn't this the WTID!?
-      h_wire: HashCode;
+      // Requested quantity
+      requested_quantity: integer;
 
-      // Time of the execution of the wire transfer by the exchange, according 
to the exchange
-      execution_time: Timestamp;
+      // Available quantity (must be below ``requested_quanitity``)
+      available_quantity: integer;
 
-      // details about the deposits
-      deposits_sums: TrackTransferDetail[];
+      // When do we expect the product to be again in stock?
+      // Optional, not given if unknown.
+      restock_expected?: timestamp;
+    }
 
-      // signature from the exchange made with purpose
-      // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT``
-      // FIXME: why return this?
-      exchange_sig: EddsaSignature;
 
-      // public EdDSA key of the exchange that was used to generate the 
signature.
-      // Should match one of the exchange's signing keys from /keys.  Again 
given
-      // explicitly as the client might otherwise be confused by clock skew as 
to
-      // which signing key was used.
-      // FIXME: why return this?
-      exchange_pub: EddsaSignature;
-    }
 
-  .. ts:def:: TrackTransferDetail
+.. http:get:: /orders
 
-    interface TrackTransferDetail {
-      // Business activity associated with the wire transferred amount
-      // ``deposit_value``.
-      order_id: string;
+  Returns known orders up to some point in the past.
 
-      // The total amount the exchange paid back for ``order_id``.
-      deposit_value: Amount;
+  **Request:**
 
-      // applicable fees for the deposit
-      deposit_fee: Amount;
-    }
+  :query paid: *Optional*. If set to yes, only return paid orders, if no only 
unpaid orders. Do not give (or use "all") to see all orders regardless of 
payment status.
+  :query refunded: *Optional*. If set to yes, only return refunded orders, if 
no only unrefunded orders. Do not give (or use "all") to see all orders 
regardless of refund status.
+  :query wired: *Optional*. If set to yes, only return wired orders, if no 
only orders with missing wire transfers. Do not give (or use "all") to see all 
orders regardless of wire transfer status.
+  :query date: *Optional.* Time threshold, see ``delta`` for its 
interpretation.  Defaults to the oldest or most recent entry, depending on 
``delta``.
+  :query start: *Optional*. Row number threshold, see ``delta`` for its 
interpretation.  Defaults to ``UINT64_MAX``, namely the biggest row id possible 
in the database.
+  :query delta: *Optional*. takes value of the form ``N (-N)``, so that at 
most ``N`` values strictly younger (older) than ``start`` and ``date`` are 
returned.  Defaults to ``-20``.
+  :query timeout_ms: *Optional*. Timeout in milli-seconds to wait for 
additional orders if the answer would otherwise be negative (long polling). 
Only useful if delta is positive. Note that the merchant MAY still return a 
response that contains fewer than delta orders.
 
+  **Response:**
 
-  **Details:**
+  :status 200 OK:
+    The response is an `OrderHistory`.
 
-  .. ts:def:: TrackTransferConflictDetails
+  .. ts:def:: OrderHistory
 
-    interface TrackTransferConflictDetails {
-      // Numerical `error code <error-codes>`
-      code: number;
+    interface OrderHistory {
+      // timestamp-sorted array of all orders matching the query.
+      // The order of the sorting depends on the sign of ``delta``.
+      orders : OrderHistory[];
+    }
 
-      // Text describing the issue for humans.
-      hint: string;
 
-      // A /deposit response matching ``coin_pub`` showing that the
-      // exchange accepted ``coin_pub`` for ``amount_with_fee``.
-      exchange_deposit_proof: DepositSuccess;
+  .. ts:def:: OrderHistoryEntry
 
-      // Offset in the ``exchange_transfer_proof`` where the
-      // exchange's response fails to match the ``exchange_deposit_proof``.
-      conflict_offset: number;
+    interface OrderHistoryEntry {
+      // The serial number this entry has in the merchant's DB.
+      row_id: number;
 
-      // The response from the exchange which tells us when the
-      // coin was returned to us, except that it does not match
-      // the expected value of the coin.
-      exchange_transfer_proof: TrackTransferResponse;
+      // order ID of the transaction related to this entry.
+      order_id: string;
 
-      // Public key of the coin for which we have conflicting information.
-      coin_pub: EddsaPublicKey;
+      // Transaction's timestamp
+      timestamp: Timestamp;
 
-      // Merchant transaction in which ``coin_pub`` was involved for which
-      // we have conflicting information.
-      transaction_id: number;
+      // Total amount the customer should pay for this order.
+      total: Amount;
 
-      // Expected value of the coin.
-      amount_with_fee: Amount;
+      // Total amount the customer did pay for this order.  Payments
+      // that were later aborted (/abort) are NOT included.
+      paid: Amount;
 
-      // Expected deposit fee of the coin.
-      deposit_fee: Amount;
+      // Total amount the customer was refunded for this order.
+      // (excludes refunds from aborts).
+      refunded: Amount;
 
-    }
+      // Was the order fully paid?
+      is_paid: boolean;
 
+    }
 
-.. http:get:: /transfers
 
-  Obtain a list of all wire transfers the backend has checked.
 
-  **Request:**
+.. http:post:: /public/orders/$ORDER_ID/claim
 
-   :query filter: FIXME: should have a way to filter, maybe even long-poll?
+  Wallet claims ownership (via nonce) over an order.  By claiming
+  an order, the wallet obtains the full contract terms, and thereby
+  implicitly also the hash of the contract terms it needs for the
+  other ``/public/`` APIs to authenticate itself as the wallet that
+  is indeed eligible to inspect this particular order's status.
 
-  **Response:**
+  **Request:**
 
-  FIXME: to be specified.
+  The request must be a `ClaimRequest`
 
+  .. ts:def:: ClaimRequest
 
+    interface ClaimRequest {
+      // Nonce to identify the wallet that claimed the order.
+      nonce: string;
+    }
 
---------------------
-Giving Customer Tips
---------------------
+  **Response:**
 
+  :status 200 OK:
+    The client has successfully claimed the order.
+    The response contains the :ref:`contract terms <ContractTerms>`.
+  :status 404 Not found:
+    The backend is unaware of the instance or order.
+  :status 409 Conflict:
+    The someone else claimed the same order ID with different nonce before.
 
-.. http:post:: /reserves
 
-  Create a reserve for tipping.
+.. http:post:: /public/orders/$ORDER_ID/pay
 
-  This request is **not** idempotent.  However, while repeating
-  it will create another reserve, that is generally pretty harmless
-  (assuming only one of the reserves is filled with a wire transfer).
-  Clients may want to eventually delete the unused reserves to
-  avoid clutter.
+  Pay for an order by giving a deposit permission for coins.  Typically used by
+  the customer's wallet.  Note that this request does not include the
+  usual ``h_contract`` argument to authenticate the wallet, as the hash of
+  the contract is implied by the signatures of the coins.  Furthermore, this
+  API doesn't really return useful information about the order.
 
   **Request:**
 
-  The request body is a `ReserveCreateRequest` object.
+  The request must be a `pay request <PayRequest>`.
 
   **Response:**
 
   :status 200 OK:
-    The backend is waiting for the reserve to be established. The merchant
-    must now perform the wire transfer indicated in the 
`ReserveCreateConfirmation`.
-  :status 424 Failed Depencency:
-    We could not obtain /wire details from the specified exchange base URL.
-
-  .. ts:def:: ReserveCreateRequest
-
-    interface ReserveCreateRequest {
-      // Amount that the merchant promises to put into the reserve
-      initial_amount: Amount;
+    The exchange accepted all of the coins.
+    The body is a `payment response <PaymentResponse>`.
+    The ``frontend`` should now fullfill the contract.
+  :status 400 Bad request:
+    Either the client request is malformed or some specific processing error
+    happened that may be the fault of the client as detailed in the JSON body
+    of the response.
+  :status 403 Forbidden:
+    One of the coin signatures was not valid.
+  :status 404 Not found:
+    The merchant backend could not find the order or the instance
+    and thus cannot process the payment.
+  :status 409 Conflict:
+    The exchange rejected the payment because a coin was already spent before.
+    The response will include the ``coin_pub`` for which the payment failed,
+    in addition to the response from the exchange to the ``/deposit`` request.
+  :status 412 Precondition Failed:
+    The given exchange is not acceptable for this merchant, as it is not in the
+    list of accepted exchanges and not audited by an approved auditor.
+  :status 424 Failed Dependency:
+    The merchant's interaction with the exchange failed in some way.
+    The client might want to try later again.
+    This includes failures like the denomination key of a coin not being
+    known to the exchange as far as the merchant can tell.
 
-      // Exchange the merchant intends to use for tipping
-      exchange_base_url: string;
+  The backend will return verbatim the error codes received from the exchange's
+  :ref:`deposit <deposit>` API.  If the wallet made a mistake, like by
+  double-spending for example, the frontend should pass the reply verbatim to
+  the browser/wallet.  If the payment was successful, the frontend MAY use
+  this to trigger some business logic.
 
-    }
+  .. ts:def:: PaymentResponse
 
-  .. ts:def:: ReserveCreateConfirmation
+    interface PaymentResponse {
+      // Signature on ``TALER_PaymentResponsePS`` with the public
+      // key of the merchant instance.
+      sig: EddsaSignature;
 
-    interface ReserveCreateConfirmation {
-      // Public key identifying the reserve
-      reserve_pub: EddsaPublicKey;
+    }
 
-      // Wire account of the exchange where to transfer the funds
-      payto_url: string;
+  .. ts:def:: PayRequest
 
+    interface PayRequest {
+      coins: CoinPaySig[];
     }
 
-.. http:get:: /reserves
+  .. ts:def:: CoinPaySig
 
-   Obtain list of reserves that have been created for tipping.
+    export interface CoinPaySig {
+      // Signature by the coin.
+      coin_sig: string;
 
-   **Request:**
+      // Public key of the coin being spend.
+      coin_pub: string;
 
-   :query after: *Optional*.  Only return reserves created after the given 
timestamp [FIXME: unit?]
+      // Signature made by the denomination public key.
+      ub_sig: string;
 
-   **Response:**
+      // The denomination public key associated with this coin.
+      denom_pub: string;
 
-  :status 200 OK:
-    Returns a list of known tipping reserves.
-    The body is a `TippingReserveStatus`.
+      // The amount that is subtracted from this coin with this payment.
+      contribution: Amount;
 
-  .. ts:def:: TippingReserveStatus
+      // URL of the exchange this coin was withdrawn from.
+      exchange_url: string;
+    }
 
-    interface TippingReserveStatus {
 
-      // Array of all known reserves (possibly empty!)
-      reserves: ReserveStatusEntry[];
+.. http:post:: /public/orders/$ORDER_ID/abort
 
-    }
+  Abort paying for an order and obtain a refund for coins that
+  were already deposited as part of a failed payment.
 
-  .. ts:def:: ReserveStatusEntry
+  **Request:**
 
-     interface ReserveStatusEntry {
+  The request must be an `abort request <AbortRequest>`.
 
-      // Public key of the reserve
-      reserve_pub: EddsaPublicKey;
+  **Response:**
 
-      // Timestamp when it was established
-      creation_time: Timestamp;
+  :status 200 OK:
+    The exchange accepted all of the coins. The body is a
+    a `merchant refund response <MerchantRefundResponse>`.
+  :status 400 Bad request:
+    Either the client request is malformed or some specific processing error
+    happened that may be the fault of the client as detailed in the JSON body
+    of the response.
+  :status 403 Forbidden:
+    The ``h_contract`` does not match the $ORDER_ID.
+  :status 404 Not found:
+    The merchant backend could not find the order or the instance
+    and thus cannot process the abort request.
+  :status 412 Precondition Failed:
+    Aborting the payment is not allowed, as the original payment did succeed.
+  :status 424 Failed Dependency:
+    The merchant's interaction with the exchange failed in some way.
+    The error from the exchange is included.
 
-      // Timestamp when it expires
-      expiration_time: Timestamp;
+  The backend will return verbatim the error codes received from the exchange's
+  :ref:`refund <_refund>` API.  The frontend should pass the replies verbatim 
to
+  the browser/wallet.
 
-      // Initial amount as per reserve creation call
-      merchant_initial_amount: Amount;
+  .. ts:def:: AbortRequest
 
-      // Initial amount as per exchange, 0 if exchange did
-      // not confirm reserve creation yet.
-      exchange_initial_amount: Amount;
+    interface AbortRequest {
 
-      // Amount picked up so far.
-      pickup_amount: Amount;
+      // hash of the order's contract terms (this is used to authenticate the
+      // wallet/customer in case $ORDER_ID is guessable).
+      h_contract: HashCode;
 
-      // Amount approved for tips that exceeds the pickup_amount.
-      committed_amount: Amount;
 
-      // Is this reserve active (false if it was deleted but not purged)
-      active: boolean;
+      // List of coins the wallet would like to see refunds for.
+      // (Should be limited to the coins for which the original
+      // payment succeeded, as far as the wallet knows.)
+      coins: AbortedCoin[];
     }
 
+    interface AbortedCoin {
+      // Public key of a coin for which the wallet is requesting an 
abort-related refund.
+      coin_pub: EddsaPublicKey;
+    }
 
-.. http:get:: /reserves/$RESERVE_PUB
 
-   Obtain information about a specific reserve that have been created for 
tipping.
 
-   **Request:**
+.. http:get:: /orders/$ORDER_ID/
 
-   :query tips: *Optional*. If set to "yes", returns also information about 
all of the tips created
+  Merchant checks the payment status of an order.  If the order exists but is 
not payed
+  yet, the response provides a redirect URL.  When the user goes to this URL,
+  they will be prompted for payment.  Differs from the ``/public/`` API both
+  in terms of what information is returned and in that the wallet must provide
+  the contract hash to authenticate, while for this API we assume that the
+  merchant is authenticated (as the endpoint is not ``/public/``).
 
-   **Response:**
+  **Request:**
 
-  :status 200 OK:
-    Returns the `ReserveDetail`.
-  :status 404 Not found:
-    The tipping reserve is not known.
-  :status 424 Failed Dependency:
-    We are having trouble with the request because of a problem with the 
exchange.
-    Likely returned with an "exchange_code" in addition to a "code" and
-    an "exchange_http_status" in addition to our own HTTP status. Also usually
-    includes the full exchange reply to our request under "exchange_reply".
-    This is only returned if there was actual trouble with the exchange, not
-    if the exchange merely did not respond yet or if it responded that the
-    reserve was not yet filled.
+  :query session_id: *Optional*. Session ID that the payment must be bound to. 
 If not specified, the payment is not session-bound.
+  :query transfer: *Optional*. If set to "YES", try to obtain the wire 
transfer status for this order from the exchange. Otherwise, the wire transfer 
status MAY be returned if it is available.
+  :query timeout_ms: *Optional*. Timeout in milli-seconds to wait for a 
payment if the answer would otherwise be negative (long polling).
 
-  .. ts:def:: ReserveDetail
+  **Response:**
 
-    interface ReserveDetail {
+  :status 200 OK:
+    Returns a `MerchantOrderStatusResponse`, whose format can differ based on 
the status of the payment.
+  :status 404 Not Found:
+    The order or instance is unknown to the backend.
+  :status 409 Conflict:
+    The exchange previously claimed that a deposit was not included in a wire
+    transfer, and now claims that it is.  This means that the exchange is
+    dishonest.  The response contains the cryptographic proof that the exchange
+    is misbehaving in the form of a `TransactionConflictProof`.
+  :status 424 Failed dependency:
+    We failed to obtain a response from the exchange about the
+    wire transfer status.
 
-      // Timestamp when it was established
-      creation_time: Timestamp;
+  .. ts:def:: MerchantOrderStatusResponse
 
-      // Timestamp when it expires
-      expiration_time: Timestamp;
+    type MerchantOrderStatusResponse = CheckPaymentPaidResponse | 
CheckPaymentUnpaidResponse
 
-      // Initial amount as per reserve creation call
-      merchant_initial_amount: Amount;
+  .. ts:def:: CheckPaymentPaidResponse
 
-      // Initial amount as per exchange, 0 if exchange did
-      // not confirm reserve creation yet.
-      exchange_initial_amount: Amount;
+    interface CheckPaymentPaidResponse {
+      paid: true;
 
-      // Amount picked up so far.
-      pickup_amount: Amount;
+      // Was the payment refunded (even partially)
+      refunded: boolean;
 
-      // Amount approved for tips that exceeds the pickup_amount.
-      committed_amount: Amount;
+      // Amount that was refunded, only present if refunded is true.
+      refund_amount?: Amount;
 
-      // Array of all tips created by this reserves (possibly empty!).
-      // Only present if asked for explicitly.
-      tips?: TipStatusEntry[];
+      // Contract terms
+      contract_terms: ContractTerms;
 
+      // If available, the wire transfer status from the exchange for this 
order
+      wire_details?: TransactionWireTransfer;
     }
 
-  .. ts:def:: TipStatusEntry
-
-    interface TipStatusEntry {
+  .. ts:def:: CheckPaymentUnpaidResponse
 
-      // Unique identifier for the tip
-      tip_id: HashCode;
+    interface CheckPaymentUnpaidResponse {
+      paid: false;
 
-      // Total amount of the tip that can be withdrawn.
-      total_amount: Amount;
+      // URI that the wallet must process to complete the payment.
+      taler_pay_uri: string;
 
-      // Human-readable reason for why the tip was granted.
-      reason: String;
+      // Alternative order ID which was paid for already in the same session.
+      // Only given if the same product was purchased before in the same 
session.
+      already_paid_order_id?: string;
 
+      // We do we NOT return the contract terms here because they may not
+      // exist in case the wallet did not yet claim them.
     }
 
+  .. ts:def:: TransactionWireTransfer
 
-.. http:post:: /reserves/$RESERVE_PUB/authorize-tip
-
-  Authorize creation of a tip from the given reserve.
-
-  **Request:**
-
-  The request body is a `TipCreateRequest` object.
+    interface TransactionWireTransfer {
 
-  **Response:**
+      // Responsible exchange
+      exchange_uri: string;
 
-  :status 200 OK:
-    A tip has been created. The backend responds with a `TipCreateConfirmation`
-  :status 404 Not Found:
-    The instance or the reserve is unknown to the backend.
-  :status 412 Precondition Failed:
-    The tip amount requested exceeds the available reserve balance for tipping.
+      // 32-byte wire transfer identifier
+      wtid: Base32;
 
-  .. ts:def:: TipCreateRequest
+      // execution time of the wire transfer
+      execution_time: Timestamp;
 
-    interface TipCreateRequest {
-      // Amount that the customer should be tipped
+      // Total amount that has been wire transfered
+      // to the merchant
       amount: Amount;
-
-      // Justification for giving the tip
-      justification: string;
-
-      // URL that the user should be directed to after tipping,
-      // will be included in the tip_token.
-      next_url: string;
     }
 
-  .. ts:def:: TipCreateConfirmation
+  .. ts:def:: TransactionConflictProof
 
-    interface TipCreateConfirmation {
-      // Unique tip identifier for the tip that was created.
-      tip_id: HashCode;
+    interface TransactionConflictProof {
+      // Numerical `error code <error-codes>`
+      code: number;
 
-      // Token that will be handed to the wallet,
-      // contains all relevant information to accept
-      // a tip.
-      tip_token: string;
+      // Human-readable error description
+      hint: string;
 
-      // URL that will directly trigger processing
-      // the tip when the browser is redirected to it
-      tip_redirect_url: string;
+      // A claim by the exchange about the transactions associated
+      // with a given wire transfer; it does not list the
+      // transaction that ``transaction_tracking_claim`` says is part
+      // of the aggregate.  This is
+      // a ``/track/transfer`` response from the exchange.
+      wtid_tracking_claim: TrackTransferResponse;
+
+      // The current claim by the exchange that the given
+      // transaction is included in the above WTID.
+      // (A response from ``/track/order``).
+      transaction_tracking_claim: TrackTransactionResponse;
+
+      // Public key of the coin for which we got conflicting information.
+      coin_pub: CoinPublicKey;
 
     }
 
 
-.. http:post:: /tips
+.. http:get:: /public/orders/$ORDER_ID/
 
-  Authorize creation of a tip from the given reserve, except with
-  automatic selection of a working reserve of the instance by the
-  backend. Intentionally otherwise identical to the /authorize-tip
-  endpoint given above.
+  Query the payment status of an order. This endpoint is for the wallet.
+  When the wallet goes to this URL and it is unpaid,
+  they will be prompted for payment.
 
   **Request:**
 
-  The request body is a `TipCreateRequest` object.
+  :query h_contract: hash of the order's contract terms (this is used to 
authenticate the wallet/customer in case $ORDER_ID is guessable). *Mandatory!*
+  :query session_id: *Optional*. Session ID that the payment must be bound to. 
 If not specified, the payment is not session-bound.
+  :query timeout_ms: *Optional.*  If specified, the merchant backend will
+    wait up to ``timeout_ms`` milliseconds for completion of the payment before
+    sending the HTTP response.  A client must never rely on this behavior, as 
the
+    merchant backend may return a response immediately.
+  :query refund=AMOUNT: *Optional*. Indicates that we are polling for a refund 
above the given AMOUNT. Only useful in combination with timeout.
 
   **Response:**
 
   :status 200 OK:
-    A tip has been created. The backend responds with a `TipCreateConfirmation`
-  :status 404 Not Found:
-    The instance is unknown to the backend.
-  :status 412 Precondition Failed:
-    The tip amount requested exceeds the available reserve balance for tipping
-    in all of the reserves of the instance.
-
+    The response is a `PublicPayStatusResponse`, with ``paid`` true.
+  :status 403 Forbidden:
+    The ``h_contract`` does not match the order.
+  :status 404 Not found:
+    The merchant backend is unaware of the order.
 
-.. http:delete:: /reserves/$RESERVE_PUB
+  .. ts:def:: PublicPayStatusResponse
 
-  Delete information about a reserve.  Fails if the reserve still has
-  committed to tips that were not yet picked up and that have not yet
-  expired.
+    interface PublicPayStatusResponse {
+      // Has the payment for this order (ever) been completed?
+      paid: boolean;
 
-  **Request:**
+      // Was the payment refunded (even partially, via refund or abort)?
+      refunded: boolean;
 
-  :query purge: *Optional*. If set to YES, the reserve and all information
-      about tips it issued will be fully deleted.
-      Otherwise only the private key would be deleted.
+      // Amount that was refunded in total.
+      refund_amount: Amount;
 
-  **Response:**
+      // Refunds for this payment, empty array for none.
+      refunds: RefundDetail[];
 
-  :status 204 No content:
-    The backend has successfully deleted the reserve.
-  :status 404 Not found:
-    The backend does not know the instance or the reserve.
-  :status 409 Conflict:
-    The backend refuses to delete the reserve (committed tips awaiting pickup).
+      // URI that the wallet must process to complete the payment.
+      taler_pay_uri: string;
 
+      // Alternative order ID which was paid for already in the same session.
+      // Only given if the same product was purchased before in the same 
session.
+      already_paid_order_id?: string;
 
+    }
 
-.. http:get:: /tips/$TIP_ID
 
-  Obtain information about a particular tip.
+.. http:delete:: /orders/$ORDER_ID
 
-   **Request:**
+  Delete information about an order.  Fails if the order was paid in the
+  last 10 years (or whatever TAX_RECORD_EXPIRATION is set to) or was
+  claimed but is unpaid and thus still a valid offer.
 
-   :query pickups: if set to "yes", returns also information about all of the 
pickups
+  **Response:**
 
-   **Response:**
+  :status 204 No content:
+    The backend has successfully deleted the order.
+  :status 404 Not found:
+    The backend does not know the instance or the order.
+  :status 409 Conflict:
+    The backend refuses to delete the order.
 
-  :status 200 OK:
-    The tip is known. The backend responds with a `TipDetails` message
-  :status 404 Not Found:
-    The tip is unknown to the backend.
 
-  .. ts:def:: TipDetails
+--------------
+Giving Refunds
+--------------
 
-    interface TipDetails {
+.. _refund:
+.. http:post:: /orders/$ORDER_ID/refund
 
-      // Amount that we authorized for this tip.
-      total_authorized: Amount;
+  Increase the refund amount associated with a given order.  The user should be
+  redirected to the ``taler_refund_url`` to trigger refund processing in the 
wallet.
 
-      // Amount that was picked up by the user already.
-      total_picked_up: Amount;
+  **Request:**
 
-      // Human-readable reason given when authorizing the tip.
-      reason: String;
+  The request body is a `RefundRequest` object.
 
-      // Timestamp indicating when the tip is set to expire (may be in the 
past).
-      expiration: Timestamp;
+  **Response:**
 
-      // Reserve public key from which the tip is funded
-      reserve_pub: EddsaPublicKey;
+  :status 200 OK:
+    The refund amount has been increased, the backend responds with a 
`MerchantRefundResponse`
+  :status 404 Not found:
+    The order is unknown to the merchant
+  :status 409 Conflict:
+    The refund amount exceeds the amount originally paid
 
-      // Array showing the pickup operations of the wallet (possibly empty!).
-      // Only present if asked for explicitly.
-      pickups?: PickupDetail[];
-    }
+  .. ts:def:: RefundRequest
 
-  .. ts:def:: PickupDetail
+    interface RefundRequest {
+      // Amount to be refunded
+      refund: Amount;
 
-    interface PickupDetail {
+      // Human-readable refund justification
+      reason: string;
+    }
 
-      // Unique identifier for the pickup operation.
-      pickup_id: HashCode;
+  .. ts:def:: MerchantRefundResponse
 
-      // Number of planchets involved.
-      num_planchets: integer;
+    interface MerchantRefundResponse {
 
-      // Total amount requested for this pickup_id.
-      requested_amount: Amount;
+      // URL (handled by the backend) that the wallet should access to
+      // trigger refund processing.
+      // taler://refund/[$H_CONTRACT/$AMOUNT????]
+      taler_refund_uri: string;
+    }
 
-      // Total amount processed by the exchange for this pickup.
-      exchange_amount: Amount;
 
-    }
 
+------------------------
+Tracking Wire Transfers
+------------------------
 
-.. http:post:: /public/tips/$TIP_ID/pickup
+.. http:post:: /transfers
 
-  Handle request from wallet to pick up a tip.
+  Inform the backend over an incoming wire transfer. The backend should 
inquire about the details with the exchange and mark the respective orders as 
wired.  Note that the request will fail if the WTID is not unique (which should 
be guaranteed by a correct exchange).
+  This request is idempotent and should also be used to merely re-fetch the
+  transfer information from the merchant's database (assuming we got a 
non-error
+  response from the exchange before).
 
   **Request:**
 
-  The request body is a `TipPickupRequest` object.
+   The request must provide `transfer information <TransferInformation>`.
 
   **Response:**
 
   :status 200 OK:
-    A tip is being returned. The backend responds with a `TipResponse`
-  :status 401 Unauthorized:
-    The tip amount requested exceeds the tip.
+    The wire transfer is known to the exchange, details about it follow in the 
body.
+    The body of the response is a `TrackTransferResponse`.  Note that
+    the similarity to the response given by the exchange for a "GET /transfer"
+    is completely intended.
+  :status 202 Accepted:
+    The exchange provided conflicting information about the transfer. Namely,
+    there is at least one deposit among the deposits aggregated by ``wtid``
+    that accounts for a coin whose
+    details don't match the details stored in merchant's database about the 
same keyed coin.
+    The response body contains the `TrackTransferConflictDetails`.
+    This is indicative of a malicious exchange that claims one thing, but did
+    something else.  (With respect to the HTTP specficiation, it is not
+    precisely that we did not act upon the request, more that the usual
+    action of filing the transaction as 'finished' does not apply.  In
+    the future, this is a case where the backend actually should report
+    the bad behavior to the auditor -- and then hope for the auditor to
+    resolve it. So in that respect, 202 is the right status code as more
+    work remains to be done for a final resolution.)
   :status 404 Not Found:
-    The tip identifier is unknown.
+    The instance is unknown to the exchange.
   :status 409 Conflict:
-    Some of the denomination key hashes of the request do not match those 
currently available from the exchange (hence there is a conflict between what 
the wallet requests and what the merchant believes the exchange can provide).
+    The wire transfer identifier is already known to us, but for a different 
amount,
+    wire method or exchange.
+  :status 424 Failed Dependency:
+    The exchange returned an error when we asked it about the "GET /transfer" 
status
+    for this wire transfer. Details of the exchange error are returned.
 
-  .. ts:def:: TipPickupRequest
+  .. ts:def:: TransferInformation
 
-    interface TipPickupRequest {
+    interface TransferInformation {
+      // how much was wired to the merchant (minus fees)
+      credit_amount: Amount;
 
-      // Identifier of the tip.
-      tip_id: HashCode;
+      // raw wire transfer identifier identifying the wire transfer (a 
base32-encoded value)
+      wtid: WireTransferIdentifierRawP;
 
-      // List of planches the wallet wants to use for the tip
-      planchets: PlanchetDetail[];
+      // target account that received the wire transfer
+      payto_uri: string;
+
+      // base URL of the exchange that made the wire transfer
+      exchange: string;
     }
 
-  .. ts:def:: PlanchetDetail
+  .. ts:def:: TrackTransferResponse
 
-    interface PlanchetDetail {
-      // Hash of the denomination's public key (hashed to reduce
-      // bandwidth consumption)
-      denom_pub_hash: HashCode;
+    interface TrackTransferResponse {
+      // Total amount transferred
+      total: Amount;
 
-      // coin's blinded public key
-      coin_ev: CoinEnvelope;
+      // Applicable wire fee that was charged
+      wire_fee: Amount;
+
+      // Time of the execution of the wire transfer by the exchange, according 
to the exchange
+      execution_time: Timestamp;
+
+      // details about the deposits
+      deposits_sums: TrackTransferDetail[];
 
     }
 
-  .. ts:def:: TipResponse
+  .. ts:def:: TrackTransferDetail
 
-    interface TipResponse {
+    interface TrackTransferDetail {
+      // Business activity associated with the wire transferred amount
+      // ``deposit_value``.
+      order_id: string;
 
-      // Blind RSA signatures over the planchets.
-      // The order of the signatures matches the planchets list.
-      blind_sigs: BlindSignature[];
+      // The total amount the exchange paid back for ``order_id``.
+      deposit_value: Amount;
+
+      // applicable fees for the deposit
+      deposit_fee: Amount;
     }
 
-    interface BlindSignature {
 
-      // The (blind) RSA signature. Still needs to be unblinded.
-      blind_sig: RsaSignature;
-    }
+  **Details:**
 
+  .. ts:def:: TrackTransferConflictDetails
+
+    interface TrackTransferConflictDetails {
+      // Numerical `error code <error-codes>`
+      code: number;
 
+      // Text describing the issue for humans.
+      hint: string;
 
---------------------------
-Dynamic Merchant Instances
---------------------------
+      // A /deposit response matching ``coin_pub`` showing that the
+      // exchange accepted ``coin_pub`` for ``amount_with_fee``.
+      exchange_deposit_proof: DepositSuccess;
 
-.. note::
+      // Offset in the ``exchange_transfer`` where the
+      // exchange's response fails to match the ``exchange_deposit_proof``.
+      conflict_offset: number;
 
-    The endpoints to dynamically manage merchant instances has not been
-    implemented yet. The bug id for this reference is #5349.
+      // The response from the exchange which tells us when the
+      // coin was returned to us, except that it does not match
+      // the expected value of the coin.
+      exchange_transfer: TrackTransferResponse;
 
-.. http:get:: /instances
+      // Proof data we have for the ``exchange_transfer`` data (signatures 
from exchange)
+      exchange_proof: TrackTransferProof;
 
-  This is used to return the list of all the merchant instances
+      // Public key of the coin for which we have conflicting information.
+      coin_pub: EddsaPublicKey;
 
-  **Response:**
+      // Merchant transaction in which ``coin_pub`` was involved for which
+      // we have conflicting information.
+      transaction_id: number;
 
-  :status 200 OK:
-    The backend has successfully returned the list of instances stored. Returns
-    a `InstancesResponse`.
+      // Expected value of the coin.
+      amount_with_fee: Amount;
 
-  .. ts:def:: InstancesResponse
+      // Expected deposit fee of the coin.
+      deposit_fee: Amount;
 
-    interface InstancesResponse {
-      // List of instances that are present in the backend (see `Instance`)
-      instances: Instance[];
     }
 
-  The `Instance` object describes the instance registered with the backend. It 
has the following structure:
+    .. ts:def:: TrackTransferProof
+
+    interface TrackTransferProof {
+      // signature from the exchange made with purpose
+      // ``TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT``
+      exchange_sig: EddsaSignature;
+
+      // public EdDSA key of the exchange that was used to generate the 
signature.
+      // Should match one of the exchange's signing keys from /keys.  Again 
given
+      // explicitly as the client might otherwise be confused by clock skew as 
to
+      // which signing key was used.
+      exchange_pub: EddsaSignature;
 
-  .. ts:def:: Instance
+      // hash of the wire details (identical for all deposits)
+      // Needed to check the ``exchange_sig``
+      h_wire: HashCode;
+    }
 
-    interface Instance {
-      // Merchant name corresponding to this instance.
-      name: string;
 
-      // The URL where the wallet will send coins.
-      // FIXME: add multi-account support here!
-      payto: string;
 
-      // Merchant instance of the response to create
-      instance: string;
+.. http:get:: /transfers
 
-      //unique key for each merchant
-      merchant_id: string;
+  Obtain a list of all wire transfers the backend has checked.
 
-      // FIXME: add locations (merchant address, jurisdiction)
-   }
+  **Request:**
 
+   :query payto_uri: *Optional*. Filter for transfers to the given bank 
account (subject and amount MUST NOT be given in the payto URI)
+   :query before: *Optional*. Filter for transfers executed before the given 
timestamp
+   :query after: *Optional*. Filter for transfers executed after the given 
timestamp
+   :query limit: *Optional*. At most return the given number of results. 
Negative for descending in execution time, positive for ascending in execution 
time.
+   :query offset: *Optional*. Starting transfer_serial_id for an iteration.
+   :query verified: *Optional*. Filter transfers by verification status.
 
-.. http:post:: /instances
+  **Response:**
 
-  This request will be used to create a new merchant instance in the backend.
+  :status 200 OK:
+    The body of the response is a `TransferList`.
 
-  **Request:**
+  .. ts:def:: TransferList
 
-  The request must be a `InstanceConfigurationMessage`.
+    interface TransferList {
+       // list of all the transfers that fit the filter that we know
+       transfers : TransferDetails[];
+    }
 
-  **Response:**
+    interface TransferDetails {
+      // how much was wired to the merchant (minus fees)
+      credit_amount: Amount;
 
-  :status 204 No content:
-    The backend has successfully created the instance.
-  :status 409 Conflict:
-    This instance already exists, but with other configuration options.
-    Use "PATCH" to update an instance configuration.
+      // raw wire transfer identifier identifying the wire transfer (a 
base32-encoded value)
+      wtid: WireTransferIdentifierRawP;
 
-  .. ts:def:: InstanceConfigurationMessage
+      // target account that received the wire transfer
+      payto_uri: string;
 
-    interface InstanceConfigurationMessage {
-      // The URL where the wallet has to send coins.
-      // payto://-URL of the merchant's bank account. Required.
-      // FIXME: need an array, and to distinguish between
-      // supported and active (see taler.conf options on accounts!)
-      payto: string;
+      // base URL of the exchange that made the wire transfer
+      exchange: string;
 
-      // Name of the merchant instance to create (will become $INSTANCE).
-      instance: string;
+      // Serial number identifying the transfer in the merchant backend.
+      // Used for filgering via ``offset``.
+      transfer_serial_id: number;
 
-      // Merchant name corresponding to this instance.
-      name: string;
+      // Time of the execution of the wire transfer by the exchange, according 
to the exchange
+      // Only provided if we did get an answer from the exchange.
+      execution_time?: Timestamp;
 
-      // FIXME: add locations (merchant address, jurisdiction)
+      // True if we checked the exchange's answer and are happy with it.
+      // False if we have an answer and are unhappy, missing if we
+      // do not have an answer from the exchange.
+      verified?: boolean;
     }
 
 
-.. http:patch:: /instances/$INSTANCE
-
-  Update the configuration of a merchant instance.
 
-  **Request**
 
-  The request must be a `InstanceConfigurationMessage`.
+--------------------
+Giving Customer Tips
+--------------------
 
-  **Response:**
+.. _tips:
+.. http:post:: /reserves
 
-  :status 204 No content:
-    The backend has successfully created the instance.
-  :status 404 Not found:
-    This instance is unknown and thus cannot be reconfigured.
+  Create a reserve for tipping.
 
+  This request is **not** idempotent.  However, while repeating
+  it will create another reserve, that is generally pretty harmless
+  (assuming only one of the reserves is filled with a wire transfer).
+  Clients may want to eventually delete the unused reserves to
+  avoid clutter.
 
-.. http:get:: /instances/$INSTANCE
+  **Request:**
 
-  This is used to query a specific merchant instance.
+  The request body is a `ReserveCreateRequest` object.
 
   **Response:**
 
   :status 200 OK:
-    The backend has successfully returned the list of instances stored. Returns
-    a `QueryInstancesResponse`.
+    The backend is waiting for the reserve to be established. The merchant
+    must now perform the wire transfer indicated in the 
`ReserveCreateConfirmation`.
+  :status 424 Failed Depencency:
+    We could not obtain /wire details from the specified exchange base URL.
 
-  .. ts:def:: QueryInstancesResponse
+  .. ts:def:: ReserveCreateRequest
 
-    interface QueryInstancesResponse {
-      // The URL where the wallet has to send coins.
-      // payto://-URL of the merchant's bank account. Required.
-      payto: string;
+    interface ReserveCreateRequest {
+      // Amount that the merchant promises to put into the reserve
+      initial_amount: Amount;
 
-      // Merchant instance of the response to create
-      // This field is optional. If it is not specified
-      // then it will automatically be created.
-      instance?: string;
+      // Exchange the merchant intends to use for tipping
+      exchange_base_url: string;
 
-      // Merchant name corresponding to this instance.
-      name: string;
+    }
 
-      // Public key of the merchant/instance, in Crockford Base32 encoding.
-      merchant_pub: EddsaPublicKey;
+  .. ts:def:: ReserveCreateConfirmation
 
-      // List of the payment targets supported by this instance. Clients can
-      // specify the desired payment target in /order requests.  Note that
-      // front-ends do not have to support wallets selecting payment targets.
-      payment_targets: string[];
+    interface ReserveCreateConfirmation {
+      // Public key identifying the reserve
+      reserve_pub: EddsaPublicKey;
 
-      // FIXME: add locations (merchant address, jurisdiction)
+      // Wire account of the exchange where to transfer the funds
+      payto_url: string;
 
     }
 
+.. http:get:: /reserves
 
-.. http:delete:: /instances/$INSTANCE
+   Obtain list of reserves that have been created for tipping.
 
-  This request will be used to delete (permanently disable)
-  or purge merchant instance in the backend. Purging will
-  delete all offers and payments associated with the instance,
-  while disabling (the default) only deletes the private key
-  and makes the instance unusuable for new orders or payments.
+   **Request:**
 
-  **Request:**
+   :query after: *Optional*.  Only return reserves created after the given 
timestamp in milliseconds
+   :query active: *Optional*.  Only return active/inactive reserves depending 
on the boolean given
+   :query failures: *Optional*.  Only return reserves where we disagree with 
the exchange about the initial balance.
 
-  :query purge: *Optional*. If set to YES, the instance will be fully
-      deleted. Otherwise only the private key would be deleted.
+   **Response:**
 
-  **Response**
+  :status 200 OK:
+    Returns a list of known tipping reserves.
+    The body is a `TippingReserveStatus`.
 
-  :status 204 NoContent:
-    The backend has successfully removed the instance.  The response is a
-    `PostInstanceRemoveResponse`.
-  :status 404 Not found:
-    The instance is unknown to the backend.
-  :status 409 Conflict:
-    The instance cannot be deleted because it has pending offers, or
-    the instance cannot be purged because it has successfully processed
-    payments that have not passed the TAX_RECORD_EXPIRATION time.
-    The latter case only applies if ``purge`` was set.
+  .. ts:def:: TippingReserveStatus
 
+    interface TippingReserveStatus {
 
+      // Array of all known reserves (possibly empty!)
+      reserves: ReserveStatusEntry[];
 
---------------------
-Inventory management
---------------------
+    }
 
-Inventory management is an *optional* backend feature that can be used to
-manage limited stocks of products and to auto-complete product descriptions
-in contracts (such that the frontends have to do less work).
+  .. ts:def:: ReserveStatusEntry
 
-.. http:get:: /products
+     interface ReserveStatusEntry {
 
-  This is used to return the list of all items in the inventory.
+      // Public key of the reserve
+      reserve_pub: EddsaPublicKey;
 
-  **Response:**
+      // Timestamp when it was established
+      creation_time: Timestamp;
 
-  :status 200 OK:
-    The backend has successfully returned the inventory. Returns
-    a `InventorySummaryResponse`.
+      // Timestamp when it expires
+      expiration_time: Timestamp;
 
-  .. ts:def:: InventorySummaryResponse
+      // Initial amount as per reserve creation call
+      merchant_initial_amount: Amount;
 
-    interface InventorySummaryResponse {
-      // List of items that are present in the inventory
-      items: InventoryEntry[];
-    }
+      // Initial amount as per exchange, 0 if exchange did
+      // not confirm reserve creation yet.
+      exchange_initial_amount: Amount;
 
-  The `InventoryEntry` object describes an item in the inventory. It has the 
following structure:
+      // Amount picked up so far.
+      pickup_amount: Amount;
 
-  .. ts:def:: InventoryEntry
+      // Amount approved for tips that exceeds the pickup_amount.
+      committed_amount: Amount;
 
-    interface InventoryEntry {
-      // Product identifier, as found in the product.
-      product_id: string;
+      // Is this reserve active (false if it was deleted but not purged)
+      active: boolean;
+    }
 
-      // Amount of the product in stock. Given in product-specific units.
-      // Set to -1 for "infinite" (i.e. for "electronic" books).
-      stock: integer;
 
-      // unit in which the product is metered (liters, kilograms, packages, 
etc.)
-      unit: string;
-    }
+.. http:get:: /reserves/$RESERVE_PUB
 
+   Obtain information about a specific reserve that have been created for 
tipping.
 
-.. http:get:: /products/$PRODUCT_ID
+   **Request:**
 
-  This is used to obtain detailed information about a product in the inventory.
+   :query tips: *Optional*. If set to "yes", returns also information about 
all of the tips created
 
-  **Response:**
+   **Response:**
 
   :status 200 OK:
-    The backend has successfully returned the inventory. Returns
-    a `ProductDetail`.
+    Returns the `ReserveDetail`.
+  :status 404 Not found:
+    The tipping reserve is not known.
+  :status 424 Failed Dependency:
+    We are having trouble with the request because of a problem with the 
exchange.
+    Likely returned with an "exchange_code" in addition to a "code" and
+    an "exchange_http_status" in addition to our own HTTP status. Also usually
+    includes the full exchange reply to our request under "exchange_reply".
+    This is only returned if there was actual trouble with the exchange, not
+    if the exchange merely did not respond yet or if it responded that the
+    reserve was not yet filled.
 
-  .. ts:def:: ProductDetail
+  .. ts:def:: ReserveDetail
 
-    interface ProductDetail {
+    interface ReserveDetail {
 
-      // Human-readable product description.
-      description: string;
+      // Timestamp when it was established
+      creation_time: Timestamp;
 
-      // Map from IETF BCP 47 language tags to localized descriptions
-      description_i18n?: { [lang_tag: string]: string };
+      // Timestamp when it expires
+      expiration_time: Timestamp;
 
-      // unit in which the product is measured (liters, kilograms, packages, 
etc.)
-      unit: string;
+      // Initial amount as per reserve creation call
+      merchant_initial_amount: Amount;
 
-      // The price for one ``unit`` of the product. Zero is used
-      // to imply that this product is not sold separately, or
-      // that the price is not fixed, and must be supplied by the
-      // front-end.  If non-zero, this price MUST include applicable
-      // taxes.
-      price: Amount;
+      // Initial amount as per exchange, 0 if exchange did
+      // not confirm reserve creation yet.
+      exchange_initial_amount: Amount;
 
-      // An optional base64-encoded product image
-      image?: ImageDataUrl;
+      // Amount picked up so far.
+      pickup_amount: Amount;
 
-      // a list of taxes paid by the merchant for one unit of this product
-      taxes: Tax[];
+      // Amount approved for tips that exceeds the pickup_amount.
+      committed_amount: Amount;
 
-      // Number of units of the product in stock in sum in total,
-      // including all existing sales ever. Given in product-specific
-      // units.
-      // A value of -1 indicates "infinite" (i.e. for "electronic" books).
-      total_stocked: integer;
+      // Array of all tips created by this reserves (possibly empty!).
+      // Only present if asked for explicitly.
+      tips?: TipStatusEntry[];
 
-      // Number of units of the product that have already been sold.
-      total_sold: integer;
+    }
 
-      // Number of units of the product that were lost (spoiled, stolen, etc.)
-      total_lost: integer;
+  .. ts:def:: TipStatusEntry
 
-      // Number of units of the product that are currently locked by some
-      // shopping cart.
-      total_locked: integer;
+    interface TipStatusEntry {
 
-      // Identifies where the product is in stock.
-      location?: Location;
+      // Unique identifier for the tip
+      tip_id: HashCode;
 
-      // Identifies when we expect the next restocking to happen.
-      next_restock?: timestamp;
+      // Total amount of the tip that can be withdrawn.
+      total_amount: Amount;
+
+      // Human-readable reason for why the tip was granted.
+      reason: String;
 
     }
 
 
-.. http:post:: /products
+.. http:post:: /reserves/$RESERVE_PUB/authorize-tip
 
-  This is used to add a product to the inventory.
+  Authorize creation of a tip from the given reserve.
 
   **Request:**
 
-  The request must be a `ProductAddDetail`.
+  The request body is a `TipCreateRequest` object.
 
   **Response:**
 
-  :status 204 No content:
-    The backend has successfully expanded the inventory.
-  :status 409 Conflict:
-    The backend already knows a product with this product ID, but with 
different details.
+  :status 200 OK:
+    A tip has been created. The backend responds with a `TipCreateConfirmation`
+  :status 404 Not Found:
+    The instance or the reserve is unknown to the backend.
+  :status 412 Precondition Failed:
+    The tip amount requested exceeds the available reserve balance for tipping.
 
+  .. ts:def:: TipCreateRequest
 
-  .. ts:def:: ProductAddDetail
+    interface TipCreateRequest {
+      // Amount that the customer should be tipped
+      amount: Amount;
 
-    interface ProductAddDetail {
+      // Justification for giving the tip
+      justification: string;
 
-      // product ID to use.
-      product_id: string;
+      // URL that the user should be directed to after tipping,
+      // will be included in the tip_token.
+      next_url: string;
+    }
 
-      // Human-readable product description.
-      description: string;
+  .. ts:def:: TipCreateConfirmation
 
-      // Map from IETF BCP 47 language tags to localized descriptions
-      description_i18n?: { [lang_tag: string]: string };
+    interface TipCreateConfirmation {
+      // Unique tip identifier for the tip that was created.
+      tip_id: HashCode;
 
-      // unit in which the product is measured (liters, kilograms, packages, 
etc.)
-      unit: string;
+      // Token that will be handed to the wallet,
+      // contains all relevant information to accept
+      // a tip.
+      tip_token: string;
 
-      // The price for one ``unit`` of the product. Zero is used
-      // to imply that this product is not sold separately, or
-      // that the price is not fixed, and must be supplied by the
-      // front-end.  If non-zero, this price MUST include applicable
-      // taxes.
-      price: Amount;
+      // URL that will directly trigger processing
+      // the tip when the browser is redirected to it
+      tip_redirect_url: string;
 
-      // An optional base64-encoded product image
-      image?: ImageDataUrl;
+    }
 
-      // a list of taxes paid by the merchant for one unit of this product
-      taxes: Tax[];
 
-      // Number of units of the product in stock in sum in total,
-      // including all existing sales ever. Given in product-specific
-      // units.
-      // A value of -1 indicates "infinite" (i.e. for "electronic" books).
-      total_stocked: integer;
+.. http:post:: /tips
 
-      // Identifies where the product is in stock.
-      location?: Location;
+  Authorize creation of a tip from the given reserve, except with
+  automatic selection of a working reserve of the instance by the
+  backend. Intentionally otherwise identical to the /authorize-tip
+  endpoint given above.
 
-      // Identifies when we expect the next restocking to happen.
-      next_restock?: timestamp;
+  **Request:**
 
-    }
+  The request body is a `TipCreateRequest` object.
 
+  **Response:**
 
+  :status 200 OK:
+    A tip has been created. The backend responds with a `TipCreateConfirmation`
+  :status 404 Not Found:
+    The instance is unknown to the backend.
+  :status 412 Precondition Failed:
+    The tip amount requested exceeds the available reserve balance for tipping
+    in all of the reserves of the instance.
 
-.. http:patch:: /products/$PRODUCT_ID
 
-  This is used to update product details in the inventory. Note that the
-  ``total_stocked`` and ``total_lost`` numbers MUST be greater or equal than
-  previous values (this design ensures idempotency).  In case stocks were lost
-  but not sold, increment the ``total_lost`` number.  All fields in the
-  request are optional, those that are not given are simply preserved (not
-  modified).  Note that the ``description_i18n`` and ``taxes`` can only be
-  modified in bulk: if it is given, all translations must be provided, not
-  only those that changed.  "never" should be used for the ``next_restock``
-  timestamp to indicate no intention/possibility of restocking, while a time
-  of zero is used to indicate "unknown".
+.. http:delete:: /reserves/$RESERVE_PUB
 
-  Limitations: you cannot remove a ``location`` from a product that used to
-  have a location.
+  Delete information about a reserve.  Fails if the reserve still has
+  committed to tips that were not yet picked up and that have not yet
+  expired.
 
   **Request:**
 
-  The request must be a `ProductPatchDetail`.
+  :query purge: *Optional*. If set to YES, the reserve and all information
+      about tips it issued will be fully deleted.
+      Otherwise only the private key would be deleted.
 
   **Response:**
 
   :status 204 No content:
-    The backend has successfully expanded the inventory.
+    The backend has successfully deleted the reserve.
+  :status 404 Not found:
+    The backend does not know the instance or the reserve.
+  :status 409 Conflict:
+    The backend refuses to delete the reserve (committed tips awaiting pickup).
 
 
-    interface ProductPatchDetail {
 
-      // Human-readable product description.
-      description?: string;
+.. http:get:: /tips/$TIP_ID
 
-      // Map from IETF BCP 47 language tags to localized descriptions
-      description_i18n?: { [lang_tag: string]: string };
+  Obtain information about a particular tip.
 
-      // unit in which the product is measured (liters, kilograms, packages, 
etc.)
-      unit?: string;
+   **Request:**
 
-      // The price for one ``unit`` of the product. Zero is used
-      // to imply that this product is not sold separately, or
-      // that the price is not fixed, and must be supplied by the
-      // front-end.  If non-zero, this price MUST include applicable
-      // taxes.
-      price?: Amount;
+   :query pickups: if set to "yes", returns also information about all of the 
pickups
 
-      // An optional base64-encoded product image
-      image?: ImageDataUrl;
+   **Response:**
 
-      // a list of taxes paid by the merchant for one unit of this product
-      taxes?: Tax[];
+  :status 200 OK:
+    The tip is known. The backend responds with a `TipDetails` message
+  :status 404 Not Found:
+    The tip is unknown to the backend.
 
-      // Number of units of the product in stock in sum in total,
-      // including all existing sales ever. Given in product-specific
-      // units.
-      // A value of -1 indicates "infinite" (i.e. for "electronic" books).
-      total_stocked?: integer;
+  .. ts:def:: TipDetails
 
-      // Number of units of the product that were lost (spoiled, stolen, etc.)
-      total_lost?: integer;
+    interface TipDetails {
 
-      // Identifies where the product is in stock.
-      location?: Location;
+      // Amount that we authorized for this tip.
+      total_authorized: Amount;
 
-      // Identifies when we expect the next restocking to happen.
-      next_restock?: timestamp;
+      // Amount that was picked up by the user already.
+      total_picked_up: Amount;
+
+      // Human-readable reason given when authorizing the tip.
+      reason: String;
+
+      // Timestamp indicating when the tip is set to expire (may be in the 
past).
+      expiration: Timestamp;
+
+      // Reserve public key from which the tip is funded
+      reserve_pub: EddsaPublicKey;
 
+      // Array showing the pickup operations of the wallet (possibly empty!).
+      // Only present if asked for explicitly.
+      pickups?: PickupDetail[];
     }
 
+  .. ts:def:: PickupDetail
 
+    interface PickupDetail {
 
-.. http:post:: /products/$PRODUCT_ID/lock
+      // Unique identifier for the pickup operation.
+      pickup_id: HashCode;
 
-  This is used to lock a certain quantity of the product for a limited
-  duration while the customer assembles a complete order.  Note that
-  frontends do not have to "unlock", they may rely on the timeout as
-  given in the ``duration`` field.  Re-posting a lock with a different
-  ``duration`` or ``quantity`` updates the existing lock for the same UUID
-  and does not result in a conflict.
+      // Number of planchets involved.
+      num_planchets: integer;
 
-  Unlocking by using a ``quantity`` of zero is is
-  optional but recommended if customers remove products from the
-  shopping cart. Note that actually POSTing to ``/orders`` with set
-  ``manage_inventory`` and using ``lock_uuid`` will **transition** the
-  lock to the newly created order (which may have a different ``duration``
-  and ``quantity`` than what was requested in the lock operation).
-  If an order is for fewer items than originally locked, the difference
-  is automatically unlocked.
+      // Total amount requested for this pickup_id.
+      requested_amount: Amount;
+
+      // Total amount processed by the exchange for this pickup.
+      exchange_amount: Amount;
+
+    }
+
+
+.. http:post:: /public/tips/$TIP_ID/pickup
+
+  Handle request from wallet to pick up a tip.
 
   **Request:**
 
-  The request must be a `LockRequest`.
+  The request body is a `TipPickupRequest` object.
 
   **Response:**
 
-  :status 204 No content:
-    The backend has successfully locked (or unlocked) the requested 
``quantity``.
-  :status 404 Not found:
-    The backend has does not know this product.
-  :status 410 Gone:
-    The backend does not have enough of product in stock.
+  :status 200 OK:
+    A tip is being returned. The backend responds with a `TipResponse`
+  :status 401 Unauthorized:
+    The tip amount requested exceeds the tip.
+  :status 404 Not Found:
+    The tip identifier is unknown.
+  :status 409 Conflict:
+    Some of the denomination key hashes of the request do not match those 
currently available from the exchange (hence there is a conflict between what 
the wallet requests and what the merchant believes the exchange can provide).
 
-  .. ts:def::LockRequest
+  .. ts:def:: TipPickupRequest
 
-    interface LockRequest {
+    interface TipPickupRequest {
 
-      // UUID that identifies the frontend performing the lock
-      lock_uuid: UUID;
+      // Identifier of the tip.
+      tip_id: HashCode;
 
-      // How long does the frontend intend to hold the lock
-      duration: time;
+      // List of planches the wallet wants to use for the tip
+      planchets: PlanchetDetail[];
+    }
 
-      // How many units should be locked?
-      quantity: integer;
+  .. ts:def:: PlanchetDetail
+
+    interface PlanchetDetail {
+      // Hash of the denomination's public key (hashed to reduce
+      // bandwidth consumption)
+      denom_pub_hash: HashCode;
+
+      // coin's blinded public key
+      coin_ev: CoinEnvelope;
+
+    }
+
+  .. ts:def:: TipResponse
+
+    interface TipResponse {
+
+      // Blind RSA signatures over the planchets.
+      // The order of the signatures matches the planchets list.
+      blind_sigs: BlindSignature[];
+    }
 
+    interface BlindSignature {
+
+      // The (blind) RSA signature. Still needs to be unblinded.
+      blind_sig: RsaSignature;
     }
 
 
+
+
+
 ------------------
 The Contract Terms
 ------------------
 
+.. _contract_terms::
+
 The contract terms must have the following structure:
 
   .. ts:def:: ContractTerms
@@ -1832,7 +1883,6 @@ The contract terms must have the following structure:
 
     interface Product {
       // merchant-internal identifier for the product.
-      // FIXME: do we require the use of the /inventory API if this is present?
       product_id?: string;
 
       // Human-readable product description.
diff --git a/taler-merchant-api-tutorial.rst b/taler-merchant-api-tutorial.rst
index ea8f72b..d42f74b 100644
--- a/taler-merchant-api-tutorial.rst
+++ b/taler-merchant-api-tutorial.rst
@@ -83,8 +83,8 @@ endpoints are prefixed with ``/public/``.
 Public Sandbox Backend and Authentication
 -----------------------------------------
 
-sandbox
-authorization
+:keywords: sandbox
+:keywords: authorization
 How the frontend authenticates to the Taler backend depends on the
 configuration. See Taler Merchant Operating Manual.
 
@@ -110,7 +110,7 @@ https://bank.demo.taler.net/.
 Merchant Instances
 ------------------
 
-instance
+:keywords: instance
 The same Taler merchant backend server can be used by multiple separate
 merchants that are separate business entities. Each of these separate
 business entities is called a *merchant instance*, and is identified by
@@ -139,7 +139,7 @@ Accepting a Simple Payment
 Creating an Order for a Payment
 -------------------------------
 
-order
+:keywords: order
 Payments in Taler revolve around an *order*, which is a machine-readable
 description of the business transaction for which the payment is to be
 made. Before accepting a Taler payment as a merchant you must create
@@ -229,7 +229,7 @@ the merchant’s obligations under the contract.
 Giving Refunds
 ==============
 
-refunds
+:keywords: refunds
 A refund in GNU Taler is a way to “undo” a payment. It needs to be
 authorized by the merchant. Refunds can be for any fraction of the
 original amount paid, but they cannot exceed the original payment.
@@ -274,12 +274,50 @@ This code snipped illustrates giving a refund:
    ...              headers={"Authorization": "ApiKey sandbox"})
    <Response [200]>
 
+
+Repurchase detection and fulfillment URLs
+=========================================
+
+:keywords: repurchase
+A possible problem for merchants selling access to digital articles
+is that a customer may have paid for an article on one device, but
+may then want to read it on a different device, possibly one that
+does not even have a Taler wallet installed.
+
+Naturally, at this point the customer would at first still be prompted to pay
+for the article again. If the customer then opens the taler://-link in the
+wallet that did previously pay for the article (for example by scanning the QR
+code on the desktop with the Android App), the wallet will claim the contract,
+detect that the fulfillment URL is identical to one that it already has made a
+payment for in the past, and initiate **repurchase redirection**: Here, the
+wallet will contact the merchant and replay the previous payment, except this
+time using the (current) session ID of the browser (it learns the session ID
+from the QR code).
+
+The merchant backend then updates the session ID of the existing order to
+the current session ID of the browser.  When the payment status for the
+"new" unpaid order is checked (or already in long-polling), the backend
+detects that for the browser's *session ID* and *fulfillment URL* there is an
+existing paid contract. It then tells the browser to immediately redirect to
+the fulfillment URL where the already paid article is available.
+
+To ensure this mechanism works as designed, merchants must make sure to not
+use the same fulfillment URL for different products or for physical products
+where customers may be expected to buy the article repeatedly.  Similarly,
+it is crucial that merchants consistently use the same fulfillment URL for
+the same digital product where repurchase detection is desired.
+
+Note that changing the session ID to a different device requires the
+involvement of the wallet that made the payment, thus reasonably limiting the
+possibility of broadly sharing the digital purchases.
+
+
 .. _Giving-Customers-Tips:
 
 Giving Customers Tips
 =====================
 
-tips
+:keywords: tips
 GNU Taler allows Web sites to grant small amounts directly to the
 visitor. The idea is that some sites may want incentivize actions such
 as filling out a survey or trying a new feature. It is important to note
@@ -692,4 +730,3 @@ locations
 
 
 .. |image0| image:: arch-api.png
-

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]