From de704e339c6674ab1fe9d1f52b2ab98bde79e5f3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 7 Nov 2020 00:49:04 +0100 Subject: work on merchant tutorial update, unfinished --- taler-merchant-api-tutorial.rst | 305 ++++++++++++++++++---------------------- 1 file changed, 136 insertions(+), 169 deletions(-) (limited to 'taler-merchant-api-tutorial.rst') diff --git a/taler-merchant-api-tutorial.rst b/taler-merchant-api-tutorial.rst index e7af9a4a..1449c9f8 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"}) + +.. 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"}) +.. 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: - -:: - - - - - Tutorial - - - -

- No wallet found. -

-

- Wallet found! -

- - - -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. -- cgit v1.2.3