diff options
Diffstat (limited to 'doc/merchant-api.content.texi')
-rw-r--r-- | doc/merchant-api.content.texi | 1068 |
1 files changed, 0 insertions, 1068 deletions
diff --git a/doc/merchant-api.content.texi b/doc/merchant-api.content.texi deleted file mode 100644 index 4ab09bda..00000000 --- a/doc/merchant-api.content.texi +++ /dev/null @@ -1,1068 +0,0 @@ -@c ***************************************** -@c This file is supposed to be included from -@c the language-specific tutorial. -@c ***************************************** - -@c Define a new index for options. -@defcodeindex op -@c Combine everything into one index (arbitrarily chosen to be the -@c concept index). -@syncodeindex op cp -@c %**end of header - -@copying -This document is a tutorial for the GNU Taler Merchant API (version @value{VERSION}, @value{UPDATED}) - -Copyright @copyright{} 2018 Taler Systems SA - -@quotation -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 or -any later version published by the Free Software Foundation; with no -Invariant Sections, with no Front-Cover Texts, and with no Back-Cover -Texts. A copy of the license is included in the section entitled -``GNU Free Documentation License''. -@end quotation -@end copying -@c If your tutorial is published on paper by the FSF, it should include -@c The standard FSF Front-Cover and Back-Cover Texts, as given in -@c maintain.texi. -@c -@c Titlepage -@c -@titlepage -@title The GNU Taler Merchant API tutorial -@subtitle Version @value{VERSION} -@subtitle @value{UPDATED} -@author Christian Grothoff (@email{christian@@grothoff.org}) -@author Marcello Stanisci (@email{marcello.stanisci@@inria.fr}) -@author Florian Dold (@email{florian.dold@@inria.fr}) -@page -@vskip 0pt plus 1filll -@insertcopying -@end titlepage - -@summarycontents -@contents - -@ifnottex -@node Top -@top The GNU Taler Merchant API Tutorial (Version for @value{LANGNAME}) -@insertcopying -@end ifnottex - - -@menu -* Introduction:: What this tutorial is about -* Accepting a Simple Payment:: How to accept simple payments -* Giving Refunds:: How to give refunds to customers -* Giving Customers Tips:: How to reward customers with tips -* Advanced topics:: Detailed solutions to specific issues - - -Appendices - -* GNU-LGPL:: The GNU Lesser General Public License says how you - can use the code of libtalermerchant.so in your own projects. -* GNU-FDL:: The GNU Free Documentation License says how you - can copy and share the documentation of GNU Taler. - -Indices - -* Concept Index:: Index of concepts and programs. -@end menu - - -@node Introduction -@chapter Introduction - -@section About GNU Taler - -GNU Taler is an open protocol for an electronic payment system with a free -software reference implementation. GNU Taler offers secure, fast and easy -payment processing using well understood cryptographic techniques. GNU Taler -allows customers to remain anonymous, while ensuring that merchants can be held -accountable by governments. Hence, GNU Taler is compatible with -anti-money-laundering (AML) and know-your-customer (KYC) regulation, as well as -data protection regulation (such as GDPR). - - -@section About this tutorial - -This tutorial addresses how to process payments using the GNU Taler merchant -Backend. This chapter explains some basic concepts. In the second chapter, you -will learn how to do basic payments. - -@clear GOT_LANG -@ifset LANG_PYTHON -@set GOT_LANG 1 -This version of the tutorial has examples for Python3. -It uses the requests library for HTTP requests. -@end ifset -@ifset LANG_CURL -@set GOT_LANG 1 -This version of the tutorial has examples for the -command line with cURL. -@end ifset -@ifset LANG_PHP -This version of the tutorial has examples for PHP, -using libcurl. -@end ifset -@c -Versions for other languages/environments are available as well. - -@cindex examples -@cindex git -If you want to look at some simple, running examples, check out these: -@itemize -@item -The @url{https://git.taler.net/blog.git/tree/talerblog/blog/blog.py, essay merchant} that -sells single chapters of a book. -@item -The @url{https://git.taler.net/donations.git/tree/talerdonations/donations/donations.py, donation page} that -accepts donations for software projects and gives donation receipts. -@item -The @url{https://git.taler.net/survey.git/tree/talersurvey/survey/survey.py,survey} that -gives users who answer a question a small reward. -@end itemize - -@section Architecture overview - -The Taler software stack for a merchant consists of the following -main components: - -@itemize -@cindex frontend -@item A frontend which interacts with the customer's browser. The - frontend enables the customer to build a shopping cart and place - an order. Upon payment, it triggers the respective business logic - to satisfy the order. This component is not included with Taler, - but rather assumed to exist at the merchant. This tutorial - describes how to develop a Taler frontend. -@cindex backend -@item A Taler-specific payment backend which makes it easy for the - frontend to process financial transactions with Taler. For this - tutorial, you will use a public sandbox backend. For production - use, you must either set up your own backend or ask another person - to do so for you. -@end itemize - -The following image illustrates the various interactions of these -key components: - -@image{arch-api, 3in} - -The backend provides the cryptographic protocol support, stores Taler-specific -financial information and communicates with the GNU Taler exchange over the -Internet. The frontend accesses the backend via a RESTful API. As a result, -the frontend never has to directly communicate with the exchange, and also does -not deal with sensitive data. In particular, the merchant's signing keys and -bank account information are encapsulated within the Taler backend. - -Some functionality of the backend (the ``public interface``) is also exposed to the -customer's browser directly. In the HTTP API, all public endpoints are prefixed with @code{/public/}. - -@section Public Sandbox Backend and Authentication -@cindex sandbox -@cindex authorization - -How the frontend authenticates to the Taler backend depends on the configuration. @xref{Top,,, manual, Taler Merchant Operating Manual}. - -The public sandbox backend @url{https://backend.demo.taler.net/} uses an API key -in the @code{Authorization} header. The value of this header must be -@code{ApiKey sandbox} for the public sandbox backend. - -@clear GOT_LANG -@ifset LANG_CURL -@set GOT_LANG 1 -@example -curl -i 'https://backend.demo.taler.net/' \ - --header "Authorization: ApiKey sandbox" -# HTTP/1.1 200 OK -# [...] -# -# Hello, I'm a merchant's Taler backend. This HTTP server is not for humans. -@end example -@end ifset -@ifset LANG_PYTHON -@set GOT_LANG 1 -@example -@verbatim ->>> import requests ->>> requests.get("https://backend.demo.taler.net", -... headers={"Authorization": "ApiKey sandbox"}) -<Response [200]> -@end verbatim -@end example -@end ifset -@ifset LANG_PHP -@set GOT_LANG 1 -@example -@verbatim -php > $c = curl_init("https://backend.demo.taler.net/"); -php > $options = array(CURLOPT_RETURNTRANSFER => true, -php ( CURLOPT_CUSTOMREQUEST => "GET", -php ( CURLOPT_HTTPHEADER => array("Authorization: ApiKey sandbox")); -php > curl_setopt_array ($c, $options); -php > $r = curl_exec ($c); -php > echo curl_getinfo ($c, CURLINFO_HTTP_CODE); -200 -php > echo $r; -Hello, I'm a merchant's Taler backend. This HTTP server is not for humans. -@end verbatim -@end example -@end ifset -@ifclear GOT_LANG -@example -(example not available for this language) -@end example -@end ifclear - -If an HTTP status code other than 200 is returned, something went wrong. You -should figure out what the problem is before continuing with this tutorial. - -The sandbox backend @url{https://backend.demo.taler.net/} uses @code{KUDOS} as -an imaginary currency. Coins denominated in @code{KUDOS} can be withdrawn -from @url{https://bank.demo.taler.net/}. - -@section Merchant Instances -@cindex 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 @emph{merchant instance}, and is identified by an -alphanumeric @emph{instance id}. If the instance is omitted, the instance id -@code{default} is assumed. - -The following merchant instances are configured on @url{https://backend.demo.taler.net/}: -@itemize -@item @code{GNUnet} (The GNUnet project) -@item @code{FSF} (The Free Software Foundation) -@item @code{Tor} (The Tor Project) -@item @code{default} (Kudos Inc.) -@end itemize - -Note that these are fictional merchants used for our demonstrators and -not affiliated with or officially approved by the respective projects. - - -@node Accepting a Simple Payment -@chapter Accepting a Simple Payment - -@section Creating an Order for a Payment -@cindex order - -Payments in Taler revolve around an @emph{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 such an order. - -This is done by posting a JSON object to the backend's @code{/order} API endpoint. At least the -following fields must be given: - -@itemize -@item @var{amount}: The amount to be paid, as a string in the format -@code{CURRENCY:DECIMAL_VALUE}, for example @code{EUR:10} for 10 Euros or -@code{KUDOS:1.5} for 1.5 KUDOS. - -@item @var{summary}: A human-readable summary for what the payment is about. -The summary should be short enough to fit into titles, though no -hard limit is enforced. - -@item @var{fulfillment_url}: A URL that will be displayed once the payment is -completed. For digital goods, this should be a page that displays the product -that was purchased. On successful payment, the wallet automatically appends -the @code{order_id} as a query parameter, as well as the @code{session_sig} for -session-bound payments (discussed later). -@end itemize - -Orders can have many more fields, see @ref{The Taler Order Format}. - -After successfully @code{POST}ing to @code{/order}, an @code{order_id} will be -returned. Together with the merchant @code{instance}, the order id uniquely -identifies the order within a merchant backend. - -@clear GOT_LANG -@ifset LANG_CURL -@set GOT_LANG 1 -@example -@verbatim -ORDER=' -{"order": { - "amount": "KUDOS:10", - "summary": "Donation", - "fulfillment_url": "https://example.com/thanks.html"}} -' - -curl -i -X POST 'https://backend.demo.taler.net/order' \ - --header "Authorization: ApiKey sandbox" -d "$ORDER" -# HTTP/1.1 200 OK -# [...] -# -# { -# "order_id": "2018.058.21.46.06-024C85K189H8P" -# } -@end verbatim -@end example -@end ifset -@ifset LANG_PYTHON -@set GOT_LANG 1 -@example -@verbatim ->>> 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, -... headers={"Authorization": "ApiKey sandbox"}) -<Response [200]> -@end verbatim -@end example -@end ifset -@ifset LANG_PHP -@set GOT_LANG 1 -@example -@verbatim -php > $c = curl_init("https://backend.demo.taler.net/order"); -php > $json = array("order"=> -php ( array("amount"=>"KUDOS:1", -php ( "fulfillent_url"=>"https://example.com/thanks.html", -php ( "summary"=>"nice product")); -php > $options = array(CURLOPT_RETURNTRANSFER=>true, -php ( CURLOPT_CUSTOMREQUEST=>"POST", -php ( CURLOPT_POSTFIELDS=>json_encode($json), -php ( CURLOPT_HTTPHEADER=>array("Authorization: ApiKey sandbox")); -php > curl_setopt_array($c, $options); -php > $r = curl_exec($c); -php > echo curl_getinfo($c, CURLINFO_HTTP_CODE); -200 -php > echo $r; -{ - "order_id": "2018.072.12.48.51-014DKDKBMHPDP" -} -@end verbatim -@end example -@end ifset -@ifclear GOT_LANG -@example -(example not available for this language) -@end example -@end ifclear - -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 @emph{contract -terms}. -@cindex contract -@cindex terms - -@section Checking Payment Status and Prompting for Payment -The status of a payment can be checked with the @code{/check-payment} endpoint. If the payment -is yet to be completed by the customer, @code{/check-payment} will give the frontend a URL (the @var{payment_redirect_url}) -that will trigger the customer's wallet to execute the payment. - -Note that the only way to obtain the @var{payment_redirect_url} is to check the status of the payment, -even if you know that the user did not pay yet. - -@clear GOT_LANG -@ifset LANG_CURL -@set GOT_LANG 1 -@example -@verbatim -ORDER_ID="2018.058.21.46.06-024C85K189H8P" -curl -i "https://backend.demo.taler.net/check-payment?order_id=$ORDER_ID" \ - --header "Authorization: ApiKey sandbox" -# HTTP/1.1 200 OK -# [...] -# -# { -# "payment_redirect_url": -# "https://backend.demo.taler.net/public/trigger-pay?[...]", -# "paid": false -# } -@end verbatim -@end example -@end ifset -@ifset LANG_PYTHON -@set GOT_LANG 1 -@example -@verbatim ->>> import requests ->>> r = requests.get("https://backend.demo.taler.net/check-payment", -... params=dict(order_id=order_resp.json()["order_id"]), -... headers={"Authorization": "ApiKey sandbox"}) ->>> print(r.json()) -@end verbatim -@end example -@end ifset -@ifset LANG_PHP -@set GOT_LANG 1 -@example -@verbatim -php > $ORDER_ID = "2018.072.12.48.51-014DKDKBMHPDP"; -php > curl_init ("https://backend.demo.taler.net/check-payment?order_id=$ORDER_ID"); -php > $options = array(CURLOPT_RETURNTRANSFER=>true, -php ( CURLOPT_CUSTOMREQUEST=>"GET", -php ( CURLOPT_HTTPHEADER=>array( -php ( "Authorization: ApiKey sandbox")); -php > curl_setopt_array($c, $options); -php > $r = curl_exec($c); -php > echo $r; -@end verbatim -@end example -@end ifset -@ifclear GOT_LANG -@example -(example not available for this language) -@end example -@end ifclear - -If the @var{paid} field in the response is @code{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: - -@itemize -@item @var{paid}: Set to @var{true}. -@item @var{contract_terms}: The full contract terms of the order. -@item @var{refunded}: @code{true} if a (possibly partial) refund was granted for this purchase. -@item @var{refunded_amount}: Amount that was refunded -@item @var{last_session_id}: Last session ID used by the customer's wallet. @xref{Session-Bound Payments}. -@end itemize - -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. - - -@node Giving Refunds -@chapter Giving Refunds -@cindex 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. -Refunds are -time-limited and can only happen while the exchange holds funds for a -particular payment in escrow. The time during which a refund is possible -can be controlled by setting the @code{refund_deadline} in an 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 @code{POST}ing to the @code{/refund} endpoint. - -The refund request JSON object has the following fields: -@itemize -@item @var{order_id}: Identifies for which order a customer should be refunded. -@c NOTE: the merchant does NOT default to instance "default". -@item @var{instance}: Merchant instance to use. -@item @var{refund}: Amount to be refunded. If a previous refund was -authorized for the same order, the new amount must be higher, otherwise -the operation has no effect. The value indicates the -total amount to be refunded, @emph{not} an increase in the refund. -@item @var{reason}: Human-readable justification for the refund. The reason is only used by the Back Office and is not exposed to the customer. -@end itemize - -If the request is successful (indicated by HTTP status code 200), the response -includes a @code{refund_redirect_url}. The frontend must redirect the -customer's browser to that URL to allow the refund to be processed by the wallet. - -This code snipped illustrates giving a refund: -@clear GOT_LANG -@ifset LANG_CURL -@set GOT_LANG 1 -@example -@verbatim -REFUND_REQ=' -{"order_id": "2018.058.21.46.06-024C85K189H8P", - "refund": "KUDOS:10", - "instance": "default", - "reason": "Customer did not like the product"} -' - -curl -i -X POST 'https://backend.demo.taler.net/refund' \ - --header "Authorization: ApiKey sandbox" -d "$REFUND_REQ" -# HTTP/1.1 200 OK -# [...] -# -# { -# [...] -# "refund_redirect_url": "[...]" -# } -@end verbatim -@end example -@end ifset -@ifset LANG_PYTHON -@set GOT_LANG 1 -@example -@verbatim ->>> import requests ->>> refund_req = dict(order_id="2018.058.21.46.06-024C85K189H8P", -... refund="KUDOS:10", -... instance="default", -... reason="Customer did not like the product") ->>> requests.post("https://backend.demo.taler.net/refund", json=refund_req, -... headers={"Authorization": "ApiKey sandbox"}) -<Response [200]> -@end verbatim -@end example -@end ifset - -@ifset LANG_PHP -@set GOT_LANG 1 -@example -@verbatim -php > $REFUND_REQ = array("order_id"=>$ORDER_ID, -php ( "refund"=>"KUDOS:0.5", -php ( "instance"=>"default", -php ( "reason"=>"Customer did not like product"); -php > $options = array(CURLOPT_RETURNTRANSFER=>true, -php ( CURLOPT_CUSTOMREQUEST=>"POST", -php ( CURLOPT_HTTPHEADER=>array( -php ( "Authorization: ApiKey sandbox"), -php ( CURLOPT_POSTFIELDS=>json_encode($REFUND_REQ)); -php > $c = curl_init("https://backend.demo.taler.net/refund"); -php > curl_setopt_array($c, $options); -php > $r = curl_exec($c); -php > echo $r; -php > echo curl_getinfo($c, CURLINFO_HTTP_CODE); -200 # Make sure you paid first! -@end verbatim -@end example -@end ifset -@ifclear GOT_LANG -@example -(example not available for this language) -@end example -@end ifclear - -@node Giving Customers Tips -@chapter Giving Customers Tips -@cindex tips - -@c NOTE: Terminology should not be merchant/customer here, as -@c the relationship is completely different. So I use -@c ``site'' and ``visitor'', as that is right now the proper -@c context. We may want to use more payment-ish terminology -@c in the future, but ``donor'' and ``grantee'' sound excessive -@c in the context of ``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 that tips are not enforceable for the visitor, as there is no -contract. It is simply a voluntary gesture of appreciation of the site -to its visitor. However, once a tip has been granted, the visitor -obtains full control over the funds provided by the site. - -The ``merchant'' backend of the site must be properly configured for -tipping, and sufficient funds must be made available for tipping @xref{Top,,, manual, -Taler Merchant Operating Manual}. - -To check if tipping is configured properly and if there are -sufficient funds available for tipping, query the @code{/tip-query} endpoint: - -@clear GOT_LANG -@ifset LANG_CURL -@set GOT_LANG 1 -@example -@verbatim -curl -i 'https://backend.demo.taler.net/tip-query?instance=default' --header "Authorization: ApiKey sandbox" -# HTTP/1.1 200 OK -# [...] -# -# { -# [...] -# "amount_available": "KUDOS:153.47", -# "amount_authorized": "KUDOS:10" -# } -@end verbatim -@end example -@end ifset -@ifset LANG_PYTHON -@set GOT_LANG 1 -@example -@verbatim ->>> import requests ->>> requests.get("https://backend.demo.taler.net/tip-query?instance=default", -... headers={"Authorization": "ApiKey sandbox"}) -<Response [200]> -@end verbatim -@end example -@end ifset -@ifset LANG_PHP -@set GOT_LANG 1 -@example -@verbatim -php > $c = curl_init("https://backend.demo.taler.net/tip-query?instance=default"); -php > $options = array(CURLOPT_RETURNTRANSFER=>true, -php ( CURLOPT_CUSTOMREQUEST=>"GET", -php ( CURLOPT_HTTPHEADER=>array( -php ( "Authorization: ApiKey sandbox")); -php > curl_setopt_array($c, $options); -php > $r = curl_exec($c); -php > echo curl_getinfo($c, CURLINFO_HTTP_CODE); -200 -@end verbatim -@end example -@end ifset -@ifclear GOT_LANG -@example -(example not available for this language) -@end example -@end ifclear - -@cindex authorize tip -To authorize a tip, @code{POST} to @code{/tip-authorize}. The following fields are recognized in the JSON -request object: - -@itemize -@item @var{amount}: Amount that should be given to the visitor as a tip. -@item @var{instance}: Merchant instance that grants the tip (each instance may have its own independend tipping funds configured). -@item @var{justification}: Description of why the tip was granted. Human-readable text not exposed to the customer, but used by the Back Office. -@item @var{next_url}: The URL that the user's browser should be redirected to by the wallet, once the tip has been processed. -@end itemize - -The response from the backend contains a @code{tip_redirect_url}. The customer's browser must be -redirected to this URL for the wallet to pick up the tip. -@cindex pick up tip - -This code snipped illustrates giving a tip: -@clear GOT_LANG -@ifset LANG_CURL -@set GOT_LANG 1 -@example -@verbatim -TIP_REQ=' -{"amount": "KUDOS:0.5", - "instance": "default", - "justification": "User filled out survey", - "next_url": "https://merchant.com/thanks.html"} -' - -curl -i -X POST 'https://backend.demo.taler.net/tip-authorize' \ - --header "Authorization: ApiKey sandbox" -d "$TIP_REQ" -# HTTP/1.1 200 OK -# [...] -# -# { -# [...] -# "tip_redirect_url": "[...]" -# } -@end verbatim -@end example -@end ifset -@ifset LANG_PYTHON -@set GOT_LANG 1 -@example -@verbatim ->>> import requests ->>> tip_req = dict(amount="KUDOS:0.5", -... instance="default", -... justification="User filled out survey", -... next_url="https://merchant.com/thanks.html") ->>> requests.post("https://backend.demo.taler.net/tip-authorize", json=tip_req, -... headers={"Authorization": "ApiKey sandbox"}) -<Response [200]> -@end verbatim -@end example -@end ifset - -@ifset LANG_PHP -@set GOT_LANG 1 -@example -@verbatim -php > $TIP_REQ = array( -php ( "amount"=>"KUDOS:1", -php ( "instance"=>"default", -php ( "justification"=>"surveying", -php ( "next_url"=>"https://example.com/survey-thanks.html"); -php > $options = array(CURLOPT_RETURNTRANSFER=>true, -php ( CURLOPT_CUSTOMREQUEST=>"POST", -php ( CURLOPT_POSTFIELDS=>json_encode($TIP_REQ), -php ( CURLOPT_HTTPHEADER=>array("Authorization: ApiKey sandbox")); -php > $c = curl_init("https://backend.demo.taler.net/tip-authorize"); -php > curl_setopt_array($c, $options); -php > $r = curl_exec($c); -php > echo curl_getinfo($c, CURLINFO_HTTP_CODE); -200 -@end verbatim -@end example -@end ifset - -@ifclear GOT_LANG -@example -(example not available for this language) -@end example -@end ifclear - - -@node Advanced topics -@chapter Advanced topics - -@menu -* Detecting the Presence of the Taler Wallet:: Detecting the Presence of the Taler Wallet -* Integration with the Back Office:: Integration with the Back Office -* Session-Bound Payments:: Session-bound payments for digital goods -* Product Identification:: Product Identification -* The Taler Order Format:: The Taler Order Format -@end menu - -@node Detecting the Presence of the Taler Wallet -@section Detecting the Presence of the Taler Wallet -@cindex 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. - -@subsection 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 @code{link} tag with the @code{id} -attribute set to @code{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: -@table @code -@item taler-installed-hide -A CSS rule will set the @code{display} property for this class to @code{none} once the Taler wallet is installed and enabled. -If the wallet is not installed, @code{display} will be @code{inherit}. - -@item taler-installed-show -A CSS rule will set the @code{display} property for this class to @code{inherit} once the Taler wallet is installed and enabled. -If the wallet is not installed, @code{display} will be @code{none}. - -@end table - -The following is a complete example: - -@smallexample -<!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> -@end smallexample - -The @code{taler-fallback.css} is part of the Taler's @emph{web-common} repository, -available at @url{https://git.taler.net/web-common.git/tree/taler-fallback.css}. -You may have to adjust the @code{href} attribute in the HTML code above to point -to the correct location of the @code{taler-fallback.css} file on your Web site. - -@subsection Detection with JavaScript - -The following functions are defined in the @code{taler} namespace of the @code{taler-wallet-lib} helper library -available at @url{https://git.taler.net/web-common.git/tree/taler-wallet-lib.js}. - -@table @code -@item onPresent(callback: () => void) -Adds a callback to be called when support for Taler payments is detected. - -@item onAbsent(callback: () => void) -Adds a callback to be called when support for Taler payments is disabled. - -@end table - -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. - -@c FIXME: include full example of Web site including taler-wallet-lib.js -@c and using JS detection actions. (alert()?) - -@node Integration with the Back Office -@section 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 @url{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 -@url{https://docs.taler.net/api/api-merchant.html} - -@node Session-Bound Payments -@section Session-Bound Payments -@cindex 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 this model by allowing the mechant to check whether the -``payment receipt'' is available on the user's current device. This -prevents users from easily sharing media access by transmitting a link -to the fulfillment page. Of course sophisticated users could share -payment receipts as well, but this is not as easy as sharing a link, -and in this case they are more likely to just share the media -directly. - -To use this feature, the merchant must first assign the user's current browser -an ephemeral @code{session_id}, usually via a session cookie. When executing -or re-playing a payment, the wallet will receive an additional signature -(@code{session_sig}). This signature certifies that the wallet -showed a payment receipt for the respective order in the current session. -@cindex cookie - -Session-bound payments are triggerd by passing the @code{session_id} parameter -to the @code{/check-payment} endpoint. The wallet will then redirect to the -fulfillment page, but include an additional @code{session_sig} parameter. The -frontend can query @code{/check-payment} with both the @code{session_id} and -the @code{session_sig} to verify that the signature is correct. - -The last session ID that was successfuly used to prove that the payment -receipt is in the user's wallet is also available as @code{last_session_id} in -the response to @code{/check-payment}. -@c FIXME: used for what? - -@node Product Identification -@section Product Identification -@cindex 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 shops without a login -system. In this case, the user would be prompted for payment again, even -though they already purchased the product. - -To allow the wallet to instead find the existing payment receipt, the -shop must use a unique fulfillment URL for each product. Then, the -frontend must provide an additional @code{resource_url} parameter to -to @code{/check-payment}. It should identify this unique fulfillment -URL for the product. The wallet will then check whether it has paid -for a contract with the same @code{resource_url} before, and if so -replay the previous payment. -@c FIXME: design question (!): why do we not simply set a flag (``unique fulfillment url'') -@c instead of passing the fulfillment URL a *second* time to the backend? -@c (and having to worry about it being the same as in the order on /order)? - - -@c Section describing the format of Taler contracts/proposals in detail - -@node The Taler Order Format -@section The Taler Order Format -@cindex contract -@cindex terms -@cindex order - -A Taler order can specify many details about the payment. -This section describes each of the fields in depth. - -Financial amounts are always specified as a string in the format @code{"CURRENCY:DECIMAL_VALUE"}. - -@table @var -@item amount -@cindex amount -Specifies the total amount to be paid to the merchant by the customer. - -@item max_fee -@cindex fees -@cindex 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 @var{amount}. - - -@item max_wire_fee -@cindex fees -@cindex 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. - - -@item wire_fee_amortization -@cindex fees -@cindex 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 the expected customer's contribution to the wire fee. -The customer's contribution may further be reduced by the difference -between the '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. - -@item pay_url -@cindex pay_url -Which URL accepts payments. This is the URL where the wallet will POST -coins. - -@item fulfillment_url -@cindex 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. - -@item order_id -@cindex order ID -Alphanumeric identifier, freely definable by the merchant. -Used by the merchant to uniquely identify the transaction. - -@item summary -@cindex 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. - -@item timestamp -Time at which the offer was generated. -@c FIXME: describe time format in detail here - -@item pay_deadline -@cindex 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 contracts are past the @var{refund_deadline} -and execute one large wire 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. - -@item refund_deadline -@cindex 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 @var{pay_deadline}. - -@item products -@cindex product description -Array of products that are being sold to the customer. Each -entry contains a tuple with the following values: - -@table @var -@item description -Description of the product. -@item quantity -Quantity of the items to be shipped. May specify a unit (@code{1 kg}) -or just the count. -@item price -Price for @var{quantity} units of this product shipped to the -given @var{delivery_location}. Note that usually the sum of all -of the prices should add up to the total amount of the contract, -but it may be different due to discounts or because individual -prices are unavailable. -@item product_id -Unique ID of the product in the merchant's catalog. Can generally -be chosen freely as it only has meaning for the merchant, but -should be a number in the range @math{[0,2^{51})}. -@item taxes -Map of applicable taxes to be paid by the merchant. The label is the -name of the tax, i.e. @var{VAT}, @var{sales tax} or @var{income tax}, -and the value is the applicable tax amount. Note that arbitrary -labels are permitted, as long as they are used to identify the -applicable tax regime. Details may be specified by the regulator. -This is used to declare to the customer which taxes the merchant -intends to pay, and can be used by the customer as a receipt. -@c FIXME: a receipt not including the item's price? -The information is also likely to be used by tax audits of the merchant. -@item delivery_date -Time by which the product is to be delivered to the -@var{delivery_location}. -@item delivery_location -This should give a label in the @var{locations} map, specifying -where the item is to be delivered. -@end table -Values can be omitted if they are not applicable. For example, if a -purchase is about a bundle of products that have no individual prices -or product IDs, the @var{product_id} or @var{price} may not be -specified in the contract. Similarly, for virtual products delivered -directly via the fulfillment URI, there is no delivery location. - -@item merchant -@table @var -@item address -This should give a label in the @var{locations} map, specifying -where the merchant is located. -@item name -This should give a human-readable name for the merchant's business. -@item jurisdiction -This should give a label in the @var{locations} map, specifying -the jurisdiction under which this contract is to be arbitrated. -@end table - -@item locations -@cindex 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 same location is required many times (such as the business -address of 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: -@table @var -@item country -Name of the country for delivery, as found on a postal package, i.e. ``France''. -@item state -Name of the state for delivery, as found on a postal package, i.e. ``NY''. -@item region -Name of the region for delivery, as found on a postal package. -@item province -Name of the province for delivery, as found on a postal package. -@item city -Name of the city for delivery, as found on a postal package. -@item ZIP code -ZIP code for delivery, as found on a postal package. -@item street -Street name for delivery, as found on a postal package. -@item street number -Street number (number of the house) for delivery, as found on a postal package. -@item name receiver name for delivery, either business or person name. - -@end table - -Note that 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. - -@end table - - -@c ********************************************************** -@c ******************* Appendices ************************* -@c ********************************************************** - -@node GNU-LGPL -@unnumbered GNU-LGPL -@cindex license -@cindex LGPL -@include lgpl.texi - -@node GNU-FDL -@unnumbered GNU-FDL -@cindex license -@cindex GNU Free Documentation License -@include fdl-1.3.texi - -@node Concept Index -@unnumbered Concept Index - -@printindex cp |