summaryrefslogtreecommitdiff
path: root/taler-merchant-api-tutorial.rst
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-11-07 00:49:04 +0100
committerChristian Grothoff <christian@grothoff.org>2020-11-07 00:49:04 +0100
commitde704e339c6674ab1fe9d1f52b2ab98bde79e5f3 (patch)
tree17f5c068097ad0d439bb6ce3cafc9c46b39731a6 /taler-merchant-api-tutorial.rst
parent655e5eb6c386b5e5bdc1c9de0831c579af5b26fe (diff)
downloaddocs-de704e339c6674ab1fe9d1f52b2ab98bde79e5f3.tar.gz
docs-de704e339c6674ab1fe9d1f52b2ab98bde79e5f3.tar.bz2
docs-de704e339c6674ab1fe9d1f52b2ab98bde79e5f3.zip
work on merchant tutorial update, unfinished
Diffstat (limited to 'taler-merchant-api-tutorial.rst')
-rw-r--r--taler-merchant-api-tutorial.rst305
1 files changed, 136 insertions, 169 deletions
diff --git a/taler-merchant-api-tutorial.rst b/taler-merchant-api-tutorial.rst
index e7af9a4..1449c9f 100644
--- a/taler-merchant-api-tutorial.rst
+++ b/taler-merchant-api-tutorial.rst
@@ -119,7 +119,7 @@ Public Sandbox Backend and Authentication
-----------------------------------------
How the frontend authenticates to the Taler backend depends on the
-configuration. See `GNU Taler Merchant Backend Operator Manual`.
+configuration. See :doc:`taler-merchant-manual`.
The public sandbox backend https://backend.demo.taler.net/ uses an API
key in the ``Authorization`` header. The value of this header must be
@@ -154,16 +154,20 @@ id ``default`` is assumed.
The following merchant instances are configured on
https://backend.demo.taler.net/:
-- ``GNUnet`` (The GNUnet project)
+- ``GNUnet`` (The GNUnet project), reachable at https://backend.demo.taler.net/instances/gnunet/
-- ``FSF`` (The Free Software Foundation)
+- ``FSF`` (The Free Software Foundation), reachable at https://backend.demo.taler.net/instances/fsf/
-- ``Tor`` (The Tor Project)
+- ``Tor`` (The Tor Project), reachable at https://backend.demo.taler.net/instances/tor/
-- ``default`` (Kudos Inc.)
+- ``default`` (Kudos Inc.), reachable at https://backend.demo.taler.net/
+
+.. Note:: These are fictional merchants used for our demonstrators and
+ not affiliated with or officially approved by the respective projects.
+
+All endpoints for instances offer the same API. Thus, which instance is
+to be used is simply included in the base URL of the merchant backend.
-Note that these are fictional merchants used for our demonstrators and
-not affiliated with or officially approved by the respective projects.
.. _Merchant-Payment-Processing:
@@ -180,8 +184,12 @@ description of the business transaction for which the payment is to be
made. Before accepting a Taler payment as a merchant you must create
such an order.
-This is done by posting a JSON object to the backend’s ``/order`` API
-endpoint. At least the following fields must be given:
+This is done by POSTing a JSON object to the backend’s ``/private/orders`` API
+endpoint. At least the following fields must be given inside the ``order``
+field:
+
+.. index:: summary
+.. index:: fulfillment URL
- amount: The amount to be paid, as a string in the format
``CURRENCY:DECIMAL_VALUE``, for example ``EUR:10`` for 10 Euros or
@@ -198,52 +206,87 @@ endpoint. At least the following fields must be given:
as the ``session_sig`` for session-bound payments (discussed later).
Orders can have many more fields, see `The Taler Order
-Format <#The-Taler-Order-Format>`__.
+Format <#The-Taler-Order-Format>`__. When POSTing an order,
+you can also specify additional details such as an override
+for the refund duration and instructions for inventory
+management. These are rarely needed and not covered in this
+tutorial, please read the :doc:`core/api-merchant` reference
+manual for details.
-After successfully ``POST``\ ing to ``/order``, an ``order_id`` will be
-returned. Together with the merchant ``instance``, the order id uniquely
-identifies the order within a merchant backend.
+A minimal Python snippet for creating an order would look like this:
::
>>> import requests
- >>> order = dict(order=dict(amount="KUDOS:10",
- ... summary="Donation",
- ... fulfillment_url="https://example.com/thanks.html"))
- >>> order_resp = requests.post("https://backend.demo.taler.net/order", json=order,
+ >>> body = dict(order=dict(amount="KUDOS:10",
+ ... summary="Donation",
+ ... fulfillment_url="https://example.com/thanks.html"),
+ ... create_token=false)
+ >>> response = requests.post("https://backend.demo.taler.net/private/order",
+ ... json=body,
... headers={"Authorization": "ApiKey sandbox"})
<Response [200]>
+
+.. index:: claim token
+
The backend will fill in some details missing in the order, such as the
address of the merchant instance. The full details are called the
-*contract terms*. contract terms
+*contract terms*.
+
+.. index:: contract terms
+
+.. note::
+ The above request disables the use of claim tokens by setting the
+ ``create_token`` option to ``false``. If you need claim tokens,
+ you must adjust the code to construct the ``taler://pay/`` URI
+ given below to include the claim token.
+
+After successfully ``POST``\ ing to ``/private/orders``, an ``order_id`` will be
+returned. Together with the merchant ``instance``, the order id uniquely
+identifies the order within a merchant backend. Using the order ID, you
+can trivially construct the respective ``taler://pay/`` URI that must
+be provided to the wallet. Let ``example.com`` be the domain name where
+the public endpoints of the instance are reachable. The Taler pay URI is
+then simply ``taler://pay/example.com/$ORDER_ID/`` where ``$ORDER_ID``
+must be replaced with the ID of the order that was returned.
+
+You can put the ``taler://`` URI as the target of a link to open the Taler
+wallet via the ``taler://`` schema, or put it into a QR code. However, for a
+Web shop, the easiest way is to simply redirect the browser to
+``https://example.com/orders/$ORDER_ID/``. That page will then trigger the
+Taler wallet. Here the backend generates the right logic to trigger the
+wallet, supporting the various types of Taler wallets in existence. Instead
+of constructing the above URL by hand, it is best to obtain it by checking for
+the payment status as described in the next section.
+
Checking Payment Status and Prompting for Payment
-------------------------------------------------
-The status of a payment can be checked with the ``/check-payment``
-endpoint. If the payment is yet to be completed by the customer,
-``/check-payment`` will give the frontend a URL (the
-payment_redirect_url) that will trigger the customer’s wallet to execute
-the payment.
+Given the order ID, the status of a payment can be checked with the
+``/private/orders/$ORDER_ID/`` endpoint. If the payment is yet to be completed
+by the customer, ``/private/orders/$ORDER_ID`` will give the frontend a URL
+(under the name ``payment_redirect_url``) that will trigger the customer’s
+wallet to execute the payment. This is basically the
+``https://example.com/orders/$ORDER_ID/`` URL we discussed above. Note
+that
-Note that the only way to obtain the payment_redirect_url is to check
-the status of the payment, even if you know that the user did not pay
-yet.
+Note that the best way to obtain the ``payment_redirect_url`` is to check the
+status of the payment, even if you know that the user did not pay yet. There
+are a few corner cases to consider when constructing this URL, so asking the
+backend to do it is the safest method.
::
>>> import requests
- >>> r = requests.get("https://backend.demo.taler.net/check-payment",
- ... params=dict(order_id=order_resp.json()["order_id"]),
+ >>> r = requests.get("https://backend.demo.taler.net/private/orders/" + order_id,
... headers={"Authorization": "ApiKey sandbox"})
>>> print(r.json())
-If the paid field in the response is ``true``, the other fields in the
-response will be different. Once the payment was completed by the user,
-the response will contain the following fields:
-
-- paid: Set to true.
+If the ``order_status`` field in the response is ``paid``, you will not
+get a ``payment_redirect_url`` and instead information about the
+payment status, including:
- contract_terms: The full contract terms of the order.
@@ -252,13 +295,18 @@ the response will contain the following fields:
- refunded_amount: Amount that was refunded
-- last_session_id: Last session ID used by the customer’s wallet. See
- `Session-Bound Payments <#Session_002dBound-Payments>`__.
-
Once the frontend has confirmed that the payment was successful, it
usually needs to trigger the business logic for the merchant to fulfill
the merchant’s obligations under the contract.
+.. Note::
+ You do not need to keep querying to notice changes
+ to the order's transaction status. The endpoint
+ support long polling, simply specify a ``timeout_ms``
+ query parameter with how long you want to wait at most
+ for the order status to change to ``paid``.
+
+
.. _Giving-Refunds:
.. index:: refunds
@@ -275,13 +323,9 @@ order. The default value for this refund deadline is specified in the
configuration of the merchant’s backend.
The frontend can instruct the merchant backend to authorize a refund by
-``POST``\ ing to the ``/refund`` endpoint.
-
-The refund request JSON object has the following fields:
+``POST``\ ing to the ``/private/orders/$ORDER_ID/refund`` endpoint.
-- order_id: Identifies for which order a customer should be refunded.
-
-- instance: Merchant instance to use.
+The refund request JSON object has only two fields:
- refund: Amount to be refunded. If a previous refund was authorized
for the same order, the new amount must be higher, otherwise the
@@ -292,7 +336,7 @@ The refund request JSON object has the following fields:
only used by the Back Office and is not exposed to the customer.
If the request is successful (indicated by HTTP status code 200), the
-response includes a ``refund_redirect_url``. The frontend must redirect
+response includes a ``taler_refund_uri``. The frontend must redirect
the customer’s browser to that URL to allow the refund to be processed
by the wallet.
@@ -301,14 +345,25 @@ This code snipped illustrates giving a refund:
::
>>> import requests
- >>> refund_req = dict(order_id="2018.058.21.46.06-024C85K189H8P",
- ... refund="KUDOS:10",
- ... instance="default",
+ >>> refund_req = dict(refund="KUDOS:10",
... reason="Customer did not like the product")
- >>> requests.post("https://backend.demo.taler.net/refund", json=refund_req,
- ... headers={"Authorization": "ApiKey sandbox"})
+ >>> requests.post("https://backend.demo.taler.net/private/orders/"
+ ... + order_id + "/refund", json=refund_req,
+ ... headers={"Authorization": "ApiKey sandbox"})
<Response [200]>
+.. Note::
+ After granting a refund, the public
+ ``https://example.com/orders/$ORDER_ID/`` endpoint will
+ change its wallet interaction from requesting payment to
+ offering a refund. Thus, frontends may again redirect
+ browsers to this endpoint. However, to do so, a
+ ``h_contract`` field must be appended
+ (``?h_contract=$H_CONTRACT``) as the public endpoint requires
+ it to authenticate the client. The required
+ ``$H_CONTRACT`` value is returned in the refund response
+ under the ``h_contract`` field.
+
.. index:: repurchase
.. _repurchase:
@@ -316,6 +371,8 @@ This code snipped illustrates giving a refund:
Repurchase detection and fulfillment URLs
=========================================
+.. TODO:: This section needs to be reviewed for 0.8.
+
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
@@ -359,6 +416,9 @@ unique.
Giving Customers Tips
=====================
+.. TODO:: This section needs to be updated for 0.8.
+
+
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
@@ -418,115 +478,15 @@ This code snipped illustrates giving a tip:
Advanced topics
===============
-.. _Detecting-the-Presence-of-the-Taler-Wallet:
-
-Detecting the Presence of the Taler Wallet
-------------------------------------------
-
-Taler offers ways to detect whether a user has the wallet installed in
-their browser. This allows Web sites to adapt accordingly. Note that not
-all platforms can do presence detection reliably. Some platforms might
-have a Taler wallet installed as a separate App instead of using a Web
-extension. In these cases, presence detection will fail. Thus, sites may
-want to allow users to request Taler payments even if a wallet could not
-be detected, especially for visitors using mobiles.
-
-Presence detection without JavaScript
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Presence detection without JavaScript is based on CSS classes. You can
-hide or show elements selectively depending on whether the wallet is
-detected or not.
-
-In order to work correctly, a special fallback stylesheet must be
-included that will be used when the wallet is not present. The
-stylesheet can be put into any file, but must be included via a ``link``
-tag with the ``id`` attribute set to ``taler-presence-stylesheet``. If a
-wallet is present, it will “hijack” this stylesheet to change how
-elements with the following classes are rendered:
-
-The following CSS classes can be used:
-
-``taler-installed-hide``
- A CSS rule will set the ``display`` property for this class to
- ``none`` once the Taler wallet is installed and enabled. If the
- wallet is not installed, ``display`` will be ``inherit``.
-
-``taler-installed-show``
- A CSS rule will set the ``display`` property for this class to
- ``inherit`` once the Taler wallet is installed and enabled. If the
- wallet is not installed, ``display`` will be ``none``.
-
-The following is a complete example:
-
-::
-
- <!DOCTYPE html>
- <html data-taler-nojs="true">
- <head>
- <title>Tutorial</title>
- <link rel="stylesheet"
- type="text/css"
- href="/web-common/taler-fallback.css"
- id="taler-presence-stylesheet" />
- </head>
- <body>
- <p class="taler-installed-hide">
- No wallet found.
- </p>
- <p class="taler-installed-show">
- Wallet found!
- </p>
- </body>
- </html>
-
-The ``taler-fallback.css`` is part of the Taler’s *web-common*
-repository, available at
-https://git.taler.net/web-common.git/tree/taler-fallback.css. You may
-have to adjust the ``href`` attribute in the HTML code above to point to
-the correct location of the ``taler-fallback.css`` file on your Web
-site.
-
-Detection with JavaScript
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following functions are defined in the ``taler`` namespace of the
-``taler-wallet-lib`` helper library available at
-https://git.taler.net/web-common.git/tree/taler-wallet-lib.js.
-
-``onPresent(callback: () => void)``
- Adds a callback to be called when support for Taler payments is
- detected.
-
-``onAbsent(callback: () => void)``
- Adds a callback to be called when support for Taler payments is
- disabled.
-
-Note that the registered callbacks may be called more than once. This
-may happen if a user disables or enables the wallet in the browser’s
-extension settings while a shop’s frontend page is open.
-
-.. _Integration-with-the-Back-Office:
-
-Integration with the Back Office
---------------------------------
-
-Taler ships a Back Office application as a stand-alone Web application.
-The Back Office has its own documentation at
-https://docs.taler.net/backoffice/html/manual.html.
-
-Developers wishing to tightly integrate back office support for
-Taler-based payments into an existing back office application should
-focus on the wire transfer tracking and transaction history sections of
-the Taler Backend API specification at
-https://docs.taler.net/api/api-merchant.html
+.. TODO:: This section needs to be updated for 0.8.
.. _Session_002dBound-Payments:
Session-Bound Payments
----------------------
-session
+.. index:: session
+
Sometimes checking if an order has been paid for is not enough. For
example, when selling access to online media, the publisher may want to
be paid for exactly the same product by each customer. Taler supports
@@ -542,7 +502,9 @@ browser an ephemeral ``session_id``, usually via a session cookie. When
executing or re-playing a payment, the wallet will receive an additional
signature (``session_sig``). This signature certifies that the wallet
showed a payment receipt for the respective order in the current
-session. cookie
+session.
+
+.. index:: cookie
Session-bound payments are triggered by passing the ``session_id``
parameter to the ``/check-payment`` endpoint. The wallet will then
@@ -560,7 +522,8 @@ in the response to ``/check-payment``.
Product Identification
----------------------
-resource url
+.. index:: resource url
+
In some situations the user may have paid for some digital good, but the
frontend does not know the exact order ID, and thus cannot instruct the
wallet to reveil the existing payment receipt. This is common for simple
@@ -575,6 +538,7 @@ the product. The wallet will then check whether it has paid for a
contract with the same ``resource_url`` before, and if so replay the
previous payment.
+
.. _The-Taler-Order-Format:
The Taler Order Format
@@ -586,29 +550,28 @@ describes each of the fields in depth.
Financial amounts are always specified as a string in the format
``"CURRENCY:DECIMAL_VALUE"``.
+
amount
- amount
Specifies the total amount to be paid to the merchant by the
customer.
+ .. index:: amount
max_fee
- fees
- maximum deposit fee
This is the maximum total amount of deposit fees that the merchant is
willing to pay. If the deposit fees for the coins exceed this amount,
the customer has to include it in the payment total. The fee is
specified using the same triplet used for amount.
+ .. index:: fees
+ .. index:: maximum deposit fee
max_wire_fee
- fees
- maximum wire fee
Maximum wire fee accepted by the merchant (customer share to be
divided by the ’wire_fee_amortization’ factor, and further reduced if
deposit fees are below ’max_fee’). Default if missing is zero.
+ .. index:: fees
+ .. index:: maximum wire fee
wire_fee_amortization
- fees
- maximum fee amortization
Over how many customer transactions does the merchant expect to
amortize wire fees on average? If the exchange’s wire fee is above
’max_wire_fee’, the difference is divided by this number to compute
@@ -617,35 +580,36 @@ wire_fee_amortization
’max_fee’ and the sum of the actual deposit fees. Optional, default
value if missing is 1. 0 and negative values are invalid and also
interpreted as 1.
+ .. index:: fees
+ .. index:: maximum fee amortization
pay_url
- pay_url
Which URL accepts payments. This is the URL where the wallet will
POST coins.
+ .. index:: pay_url
fulfillment_url
- fulfillment URL
Which URL should the wallet go to for obtaining the fulfillment, for
example the HTML or PDF of an article that was bought, or an order
tracking system for shipments, or a simple human-readable Web page
indicating the status of the contract.
+ .. index:: fulfillment URL
order_id
- order ID
Alphanumeric identifier, freely definable by the merchant. Used by
the merchant to uniquely identify the transaction.
+ .. index:: order ID
summary
- summary
Short, human-readable summary of the contract. To be used when
displaying the contract in just one line, for example in the
transaction history of the customer.
+ .. index:: summary
timestamp
Time at which the offer was generated.
pay_deadline
- payment deadline
Timestamp of the time by which the merchant wants the exchange to
definitively wire the money due from this contract. Once this
deadline expires, the exchange will aggregate all deposits where the
@@ -653,20 +617,21 @@ pay_deadline
payment for them. Amounts will be rounded down to the wire transfer
unit; if the total amount is still below the wire transfer unit, it
will not be disbursed.
+ .. index:: payment deadline
refund_deadline
- refund deadline
Timestamp until which the merchant willing (and able) to give refunds
for the contract using Taler. Note that the Taler exchange will hold
the payment in escrow at least until this deadline. Until this time,
the merchant will be able to sign a message to trigger a refund to
the customer. After this time, it will no longer be possible to
refund the customer. Must be smaller than the pay_deadline.
+ .. index:: refund deadline
products
- product description
Array of products that are being sold to the customer. Each entry
contains a tuple with the following values:
+ .. index:: product description
description
Description of the product.
@@ -725,7 +690,6 @@ merchant
jurisdiction under which this contract is to be arbitrated.
locations
- location
Associative map of locations used in the contract. Labels for
locations in this map can be freely chosen and used whenever a
location is required in other parts of the contract. This way, if the
@@ -733,6 +697,7 @@ locations
the customer or the merchant), it only needs to be listed (and
transmitted) once, and can otherwise be referred to via the label. A
non-exhaustive list of location attributes is the following:
+ .. index:: location
name
receiver name for delivery, either business or person name.
@@ -764,7 +729,9 @@ locations
Street number (number of the house) for delivery, as found on a
postal package.
- Note that locations are not required to specify all of these fields,
+
+.. Note::
+ Locations are not required to specify all of these fields,
and they is also allowed to have additional fields. Contract
renderers must render at least the fields listed above, and should
render fields that they do not understand as a key-value list.