summaryrefslogtreecommitdiff
path: root/integration-merchant.rst
blob: f3221617e1d565f76f4634ba9dcac39203912d5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
..
  This file is part of GNU TALER.

..
  Note that this page is more a protocol-explaination than a guide that teaches
  merchants how to work with Taler wallets

  Copyright (C) 2014, 2015, 2016 INRIA

  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU General Public License as published by the Free Software
  Foundation; either version 2.1, or (at your option) any later version.

  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License along with
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

  @author Marcello Stanisci
  @author Christian Grothoff

==================================
Interaction with merchant websites
==================================

.. _payprot:

+++++++++++++++++++
The payment process
+++++++++++++++++++

By design, the Taler payment process ensures the following properties:

1. The user must see and accept a contract in a secure context before the payment happens.
   That contract accounts for all the items which are supposed to be bought.

2. The payment process must be idempotent, that is at any later time the customer must
   be able to replay the payment and retrieve the resource he paid for.
   In case where a physical item was bought, this online resource is the merchant's
   order status page, which may contain tracking information for the customer.
   Note that by `replaying the payment` we mean reusing the `same coins` used to pay for
   the product the first time to get the `same product` the user got the first time.
   So the replay does NOT subtract further credit from the user's total budget.

3. Purchases are shareable: any purchase is given a URL that allows other users to
   buy the same item(s).

We call an *offer URL* any URL at the merchant's Web site that notifies the
wallet that the user needs to pay for something. The offer URL must take into
account that the user has no wallet installed, and manage the situation accordingly
(for example, by showing a credit card paywall).  The notification can happen either
via JavaScript or via HTTP headers.

The merchant needs to have a *contract URL* which generates the JSON
contract for Taler.  Alternatively, the contract may be embedded
within the page returned by the offer URL and given to the wallet
via JavaScript or via an HTTP header.

The merchant must also provide a *pay URL* to which the wallet can
transmit the payment. Again, how this URL is made known from the merchant
to the wallet, it is managed by the HTTP headers- or JavaScript-based protocol.

The merchant must also have a *fulfillment URL*, that addresses points 2 and 3 above.
In particular, fulfillment URL is responsible for:

* Deliver the final product to the user after the payment
* Instruct the wallet to send the payment to the pay URL
* Redirect the user to the offer URL in case they hit a shared fulfillment URL.

Again, Taler provides two ways of doing that: JavaScript- and HTTP headers-based.

Taler helps merchants on the JavaScript-based interaction by providing the
``taler-wallet-lib``.  See https://git.taler.net/web-common.git/tree/taler-wallet-lib.ts

-------
Example
-------

For example, suppose Alice wants to pay for a movie.  She will first
select the movie from the catalog, which directs her to the offer URL
*https://merchant/offer?x=8ru42*.  This URL generates a "402 Payment
Required" response, and will instruct the wallet about the contract's
URL. Then the wallet downloads the contract that states that Alice is
about to buy a movie.  The contract includes a fresh transaction ID, say 62.
Alice's browser detects the response code and displays the contract
for Alice.

Alice then confirms that she wants to buy the movie. Her wallet
associates her confirmation with the details and a hash of the contract.
After Alice confirms, the wallet redirects her to the fulfillment URL, say
*https://merchant/fulfillment?x=8ru42&tid=62* that is specified in the
contract.

The first time Alice visits this URL, the merchant will again
generate a "402 Payment Required" response, this time not including
the full contract but merely the hash of the contract (which includes
Alice's transaction ID 62), as well as the offer URL (which Alice
will ignore) and the pay URL.  Alice's wallet will detect that
Alice already confirmed that she wants to execute this particular
contract.  The wallet will then transmit the payment to the pay URL,
obtain a response from the merchant confirming that the payment was
successful, and then reload the fulfillment URL.

This time (and every time in the future where Alice visits the
fulfillment URL), she receives the movie.  If the browser has lost the
session state, the merchant will again ask her to pay (as it happened the
very first time she visited the fulfillment URL), and she will authenticate
by replaying the payment.

If Alice decides to share the fulfillment URL with Bob and he visits
it, his browser will not have the right session state and furthermore
his wallet will not be able to replay the payment. Instead, his wallet
will automatically redirect Bob to the offer URL and allow him to
purchase the movie himself.

.. _offer:

---------------
Making an offer
---------------

When a user visits a offer URL, the merchant returns a page that can interact
with the wallet either via JavaScript or by returning a "402 Payment Required".
This page's main objective is to inform the wallet on where it should get the
contract.  In case of JavaScript interaction, the merchant should just return
a page whose javascript contains an invocation to ``offerContractFrom(<CONTRACT-URL>)``
from ``taler-wallet-lib``.  This function will download the contract from
`<CONTRACT-URL>` and hand it to the wallet.

In case of HTTP headers-based protocol, the merchant needs to set the header
`X-Taler-contract-url` to the contract URL.  Once this information reaches the
browser, the wallet will takes action by reading that header and downloading
the contract.

Either way, the contract gets to the wallet which then renders it to the user.

.. _fulfillment:

-------------------------------
Fulfillment interaction details
-------------------------------

A payment process is triggered whenever the user visits a fulfillment
URL and he has no rights in the session state to get the items
accounted in the fulfillment URL. Note that after the user accepts a
contract, the wallet will automatically point the browser to the
fulfillment URL.

Becasue fulfillment URLs implements replayable and shareable payments
(see points 2,3 above), fulfillment URL parameter must encompass all the
details necessary to reconstruct a contract.

That saves the merchant from writing contracts to disk upon every contract
generation, and defer this operation until customers actually pay.

..................
HTTP headers based
..................

Once the fulfillment URL gets visited, deliver the final product if the user has
paid, otherwise: the merchant will reconstruct the contract and re-hash it, sending
back to the client a "402 Payment required" status code and some HTTP headers which
will help the wallet to manage the payment.  Namely:

* `X-taler-contract-hash`
* `X-taler-pay-URL`
* `X-taler-offer-URL`

The wallet then looks at `X-taler-contract-hash`, and can face two situations:

1. This hashcode is already present in the wallet's database (meaning that the user did accept the related contract), so the wallet can send the payment to `X-taler-pay-URL`.  During this operation, the wallet associates the coins it sent to `X-taler-pay-URL` with this hashcode, so that it can replay payments whenever it gets this hashcode again.

2. This hashcode is unknown to the wallet (meaning that the user visited a shared fulfillment URL). The wallet then points the browser to `X-taler-offer-URL`, which is in charge of generating a contract referring to the same items accounted in the fulfillment URL.  Of course, the user is then able to accept or not the contract.

................
JavaScript based
................

Once the fulfillment URL gets visited, deliver the final product if the user has paid, otherwise:
the merchant will reconstruct the contract and re-hash it. Then it will return a page whose JavaScript
needs to include a call to ``taler.executeContract(..)``. See the following example:

.. sourcecode:: html

  <html>
    <head>
      <script src="path/to/taler-wallet-lib.js"></script>
      <script type="application/javascript">
        // Imported from taler-wallet-lib.js
        taler.executePayment(<CONTRACT-HASHCODE>, <PAY-URL>, <OFFERING-URL>);
      </script>
    </head>
    ..
    
  </html>

The logic which will take place is the same as in the HTTP header based protocol.
Once ``executePayment(..)`` gets executed in the browser, it will hand its three
parameters to the wallet, which will:

1. Send the payment to `<PAY-URL>` if `<CONTRACT-HASH>` is found in its database (meaning that the user accepted it).
2. Redirect the browser to `<OFFER-URL>`, if `<CONTRACT-HASH>` is NOT found in its database, meaning that the user visited a shared fulfillment URL.

--------------------
Example: Essay Store
--------------------

This section is a high-level description of a merchant :ref:`frontend <merchant-arch>`,
and is inspired by our demonstration essay store running at `https://blog.demo.taler.net/`.
Basically, it tells how the frontend reacts to clients visiting `offer` and `fulfillment`
URLs.

The website is implemented in Python+Flask, and is available at
https://git.taler.net/merchant-frontends.git/tree/talerfrontends/blog.

The desired effect is that the homepage has a list of buyable articles, and once the
user clicks on one of them, they will either get the Taler :ref:`contract <contract>`
or a credit card paywall if they have no Taler wallet installed.

In particular, any buyable article on the homepage links to an `offer URL`:

.. sourcecode:: html

  <html>
    ...
    <h3><a href="/essay/How_to_write_a_frontend">How to write a frontend</a></h3>
    ...
  </html>

whence the offer URL design is as follows::

  https://<BASEURL>/essay/<ARTICLE-NAME>

`<ARTICLE-NAME>` is just a token that uniquely identifies the article within the shop.

The server-side handler for the offer URL will return a special page to the client that
will either HTTP GET the contract from the frontend, or show the credit card paywall. 
See `above <offer>`_ how this special page works.

It is interesting to note that the fulfillment URL is just the offer URL plus
two additional parameters. It looks as follows::

  https://<BASEURL>/essay/<ARTICLE-NAME>?tid=<TRANSACTION-ID>&timestamp=<TIMESTAMP>

.. note::

  Taler does not require that offer and fulfillment URL have this kind of relationship.
  In fact, it is perfectly acceptable for the fulfillment URL to be hosted on a different
  server under a different domain name.

The fulfillment URL server-side handler implements the following logic: it checks the state
to see if `<ARTICLE-NAME>` has been payed, and if so, returns the article to the user.
If the user didn't pay, then it `executes` the contract by returning a special page to the
browser. The contract execution is the order to pay that the frontend gives to the wallet.

Basically, the frontend points the wallet to the hashcode of the contract which is to be paid
and the wallet responds by giving coins to the frontend. Because the frontend doesn't perform
any cryptographic work by design, it forwards `<ARTICLE-NAME>`, `<TRANSACTION-ID>` and
`<TIMESTAMP>` to the frontend in order to get the contract's hashcode.

See `above <fulfillment>`_ for a detailed description of how the frontend triggers the
payment in the wallet.

..................
State and security
..................

The server-side state gets updated in two situations, (1) when an article is
"about" to be bought, which means when the user visits the fulfillment URL,
and (2) when the user actually pays.  For (1), we use the contract hascode to
access the state, whereas in (2) we just define a list of payed articles.
For example:

.. sourcecode:: python

  session[<HASHCODE>] = {'article_name': 'How_to_write_a_frontend'} # (1)
  session['payed_articles'] = ['How_to_write_a_frontend', 'How_to_install_a_backend'] # (2)

The list of payed articles is used by the frontend to deliver the article to the user:
if the article name is among ``session['payed_articles']``, then the user gets what they
paid for.

The reason for using `<HASHCODE>` as the key is to prevent the wallet to send bogus
parameters along the fulfillment URL.  `<HASHCODE>` is the contract hashcode that
the fulfillment handler gets from the backend using the fulfillment URL parameters.

In fact, when the wallet sends the payment to the frontend pay handler, it has to provide
both coins and contract hashcode.  That hascode is (1) verified by the backend when it
receives the coins, (2) used by the frontend to update the list of payed articles.

See below an example of pay handler:

.. sourcecode:: python

  ...

  # 'deposit_permission' is the JSON object sent by the wallet
  # which contains coins and the contract hashcode.
  response = send_payment_to_backend(deposit_permission)

  # The backend accepted the payment
  if 200 == response.status_code:
      # Here we pick the article name from the state defined at
      # fulfillment time.
      # deposit_permission['H_contract'] is the contract hashcode
      payed_article = session[deposit_permission['H_contract']]['article_name']
      session['payed_articles'].append(payed_article)
      

So the wallet is forced to send a valid contract hashcode along the payment,
and since that hashcode is then used to update the list of payed articles,
the wallet is forced to send fulfillment URL parameters that match that hashcode,
therefore being valid parameters.