taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

taler-nfc-guide.rst (10355B)


      1 GNU Taler NFC Guide
      2 ###################
      3 
      4 This guide explains how NFC (near-field communication) is
      5 used in the GNU Taler payment system.
      6 
      7 Introduction
      8 ============
      9 
     10 NFC is currently used for two different purposes:
     11 
     12 1. Operations in the wallet (payment, withdrawal, ...) can be triggered by a
     13    merchant PoS (Point-of-Sale) terminal or Taler-capable ATM.
     14 2. When either the wallet or the merchant do not have Internet connectivity,
     15    the protocol messages to the exchange or merchant backend service can be
     16    tunneled via NFC through the party that has Internet connectivity.
     17 
     18 
     19 Background: Payment Processing with GNU Taler
     20 =============================================
     21 
     22 The following steps show a simple payment process with GNU Taler.  Examples are
     23 written in `Bash <https://www.gnu.org/software/bash/>`_ syntax,
     24 using `curl <https://curl.haxx.se/docs/manpage.html>`_ to make HTTP(S) requests.
     25 They make use of the :http:post:`[/instances/$INSTANCE]/private/orders`
     26 and :http:get:`[/instances/$INSTANCE]/private/orders` endpoints.
     27 
     28 1. The merchant creates an *order*, which contains the details of the payment
     29    and the product/service that the customer will receive.
     30    An order is identified by an alphanumeric *order ID*.
     31 
     32    The *fulfillment URL* is an URL that the wallet will redirect the customer
     33    to once the payment is complete.  For digital products, this is typically an
     34    ``https(s)://`` URL that renders the purchased content.  For physical
     35    products and in-store purchases, a ``taler://fulfillment-success/<message>``
     36    URL should be specified instead.  The wallet will display the URL-encoded
     37    UTF-8 text ``<message>`` when the payment has succeeded.
     38 
     39    .. hint::
     40 
     41      When an ``http(s)://`` URL is used as the fulfillment URL in an in-store / NFC payment,
     42      the user might not be able to view the page, as request tunneling only works for requests
     43      made by the wallet to the merchant backend / exchange.
     44 
     45      In these situations, wallets should display to the user that a page to view the purchase
     46      can be opened, and give a warning if it is detected that the devices does not have Internet
     47      connectivity.
     48 
     49    The following POST ``/private/orders`` request to the merchant backend creates a
     50    simple order:
     51 
     52    .. code-block:: console
     53 
     54     $ backend_base_url=https://backend.demo.taler.net/
     55     $ auth_header='Authorization: ApiKey sandbox'
     56     $ order_req=$(cat <<EOF
     57       {
     58         "order": {
     59           "summary": "one ice cream",
     60           "amount": "KUDOS:1.5",
     61           "fulfillment_url":
     62             "taler://fulfillment-success/Enjoy+your+ice+cream!"
     63        }
     64       }
     65     EOF
     66     )
     67     $ curl -XPOST -H"$auth_header" -d "$order_req" "$backend_base_url"/private/orders
     68     {
     69       "order_id": "2019.255-02YDHMXCBQP6J"
     70     }
     71 
     72 2. The merchant checks the payment status of the order using
     73    GET ``/private/orders/$ORDER_ID``:
     74 
     75    .. code-block:: console
     76 
     77      $ backend_base_url=https://backend.demo.taler.net/
     78      $ auth_header='Authorization: ApiKey sandbox'
     79      $ curl -XGET -H"$auth_header" \
     80         "$backend_base_url/private/orders/2019.255-02YDHMXCBQP6J"
     81      # Response:
     82      {
     83        "taler_pay_uri": "taler://pay/backend.demo.taler.net/-/-/2019.255-02YDHMXCBQP6J",
     84        "paid": false,
     85        # ... (some fields omitted)
     86      }
     87 
     88    As expected, the order is not paid.  To actually proceed with the payment, the value of ``taler_pay_uri``
     89    must be processed by the customer's wallet.  There are multiple ways for the wallet to obtain the ``taler://pay/`` URI
     90 
     91    * in a QR code
     92    * in the ``Taler:`` HTTP header of a Web site
     93    * by manually entering it in the command-line wallet
     94    * **via NFC** (explained in this guide)
     95 
     96    The details of ``taler://`` URIs are specified in `LSD 0006 <https://lsd.gnunet.org/lsd0006/>`_.
     97 
     98 3. The wallet processes the ``taler://pay/`` URI.  In this example, we use the
     99    command-line wallet:
    100 
    101    .. code-block:: console
    102 
    103      # Withdraw some toy money (KUDOS) from the demo bank
    104      $ taler-wallet-cli test-withdraw \
    105        -e https://exchange.demo.taler.net/ \
    106        -b https://bank.demo.taler.net/ \
    107        -a KUDOS:10
    108      # Pay for the order from the merchant.
    109      $ taler-wallet-cli pay-uri 'taler://pay/backend.demo.taler.net/-/-/2019.255-02YDHMXCBQP6J'
    110      # [... User is asked to confirm the payment ...]
    111 
    112    .. hint::
    113 
    114      The command-line wallet is typically used by developers and not by end-users.
    115      See the :ref:`wallet manual <command-line-wallet>` for installation instructions.
    116 
    117 
    118 4. The merchant checks the payment status again:
    119 
    120    .. code-block:: console
    121 
    122      $ backend_base_url=https://backend.demo.taler.net/
    123      $ auth_header='Authorization: ApiKey sandbox'
    124      $ curl -XGET -H"$auth_header" \
    125         "$backend_base_url/private/orders/2019.255-02YDHMXCBQP6J"
    126      # Response:
    127      {
    128        "paid": true,
    129        # ... (some fields omitted)
    130      }
    131 
    132    .. note::
    133 
    134      When paying for digital products displayed on a Web site identified by the
    135      fulfillment URL, the merchant only needs to check the payment status
    136      before responding with the fulfillment page.
    137 
    138      For in-store payments, the merchant must periodically check the payment status.
    139      Instead of polling in a busy loop, the ``timeout_ms`` parameter
    140      of GET ``/private/orders/$ORDER_ID``
    141      should be used.
    142 
    143 
    144 Taler NFC Basics
    145 ================
    146 
    147 The NFC communication in GNU Taler follows the ISO-DEP (`ISO 14443-4
    148 <https://www.iso.org/standard/73599.html>`_) standard.  The wallet always acts
    149 as a tag (or more precisely, emulated card), while the merchant PoS terminal
    150 and bank terminal act as a reader.
    151 
    152 The basic communication unit is the application protocol data unit (`APDU
    153 <https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit>`_), with the structure
    154 and commands defined in `ISO 7816 <https://cardwerk.com/iso-7816-smart-card-standard>`_.
    155 
    156 The GNU Taler wallet uses the AID (application identifier) ``F00054414c4552``.
    157 The ``F`` prefix indicates the proprietary/unregistered namespace of AIDs, and
    158 the rest of the identifier is the hex-encoded ASCII-string ``TALER`` (with one
    159 0-byte left padding).
    160 
    161 During the time that the wallet is paired with a reader, there is state
    162 associated with the communication channel. Most importantly, the first message
    163 sent by the reader to the wallet must be a ``SELECT FILE (=0xA4)`` that selects
    164 the GNU Taler AID.  Messages that are sent before the correct ``SELECT FILE``
    165 message results in implementation-defined behavior, such as the tag disconnecting,
    166 ignoring the message or an app other than the wallet receiving the message.
    167 
    168 The reader sends commands to the wallet with the ``PUT DATA (=0xDA)``
    169 instruction, using the instruction parameters ``0x0100``, denoting a
    170 proprietary instruction.
    171 
    172 The command data of the ``PUT DATA`` APDU is prefixed by a one-byte Taler
    173 instruction ID (TID).  Currently, the following TIDs are used:
    174 
    175 .. list-table::
    176   :widths: 5 50
    177   :header-rows: 1
    178 
    179   * - TID (reader to wallet)
    180     - Description
    181   * - ``0x01``
    182     - Dereference the UTF-8 encoded ``taler://`` URI in the remainder of the command data.
    183   * - ``0x02``
    184     - Accept the UTF-8 encoded JSON object in the remainder of the command data as a request tunneling response.
    185 
    186 
    187 The ``GET DATA (=0xCA)`` instruction (again with the instruction parameters
    188 ``0x0100`` is used to request a command from the wallet.  The APDU with this
    189 instruction must be sent with a ``0x0000`` trailer to indicate that up to 65536
    190 bytes of data are expected in the response from the wallet.  Note that the
    191 wallet itself cannot initiate communication, and thus the reader must "poll"
    192 the wallet for commands.
    193 
    194 The response to the ``GET DATA`` instruction has a Taler instruction ID in the
    195 first byte.  The rest of the
    196 body is interpreted depending on the TID.
    197 
    198 .. list-table::
    199   :widths: 15 50
    200   :header-rows: 1
    201 
    202   * - TID
    203       (wallet to reader)
    204     - Description
    205   * - ``0x03``
    206     - Accept the UTF-8 encoded JSON object in the remainder of the command data as a request tunneling request.
    207 
    208 
    209 Sending taler:// URIs to the Wallet via NFC
    210 ===========================================
    211 
    212 To make the wallet process a ``taler://`` URI via NFC, the merchant PoS
    213 terminal sends a ``SELECT FILE`` command with the GNU Taler AID, and a ``PUT
    214 DATA`` command with TID ``0x01`` and the URI in the rest
    215 of the command data.
    216 
    217 Here is an example protocol trace from an interaction which caused the wallet
    218 to dereference the ``taler://pay`` URI from the example above:
    219 
    220 .. code-block:: none
    221 
    222   # SELECT FILE
    223   m->w 00A4040007F00054414c4552
    224   # success response with no data
    225   m<-w 9000
    226 
    227   # PUT DATA (TID=0x01)
    228   m->w 00DA01007c0174616c65723a2f2f7061792f6261636b656e642e64656d6f2e74
    229        616c65722e6e65742f2d2f2d2f323031392e3235352d30325944484d58434251
    230        50364a
    231   # success response with no data
    232   m<-w 9000
    233 
    234 (Note that this process works analogously for communication with a bank/ATM
    235 terminal.)
    236 
    237 
    238 Request tunneling
    239 =================
    240 
    241 Request tunneling allows tunneling a (very) restricted subset of HTTP through
    242 NFC. In particular, only JSON request and response bodies are allowed.
    243 
    244 It is currently assumed that the requests and responses fit into one APDU frame.
    245 For devices with more limited maximum APDU sizes, additional TIDs for segmented
    246 tunnel requests/responses may be defined in the future.
    247 
    248 A request for tunneling is initiated with TID ``0x03`` and responded to with
    249 TID ``0x02`` (see tables above).  A tunneling request is identified by a
    250 numeric ID, which must be unique during one pairing between reader and tag.
    251 
    252 The request tunneling request/response JSON messages have the following schema:
    253 
    254 .. code-block:: tsref
    255 
    256   interface TalerRequestTunnelRequest {
    257     // Identifier for the request
    258     id: number;
    259 
    260     // Request URL
    261     url: string;
    262 
    263     // HTTP method to use
    264     method: "post" | "get";
    265 
    266     // Request headers
    267     headers?: { [name: string]: string };
    268 
    269     // JSON body for the request, only applicable to POST requests
    270     body?: object;
    271   }
    272 
    273   interface TalerRequestTunnelResponse {
    274     // Identifier for the request
    275     id: number;
    276 
    277     // Response HTTP status code,
    278     // "0" if there was no response.
    279     status: number;
    280 
    281     // JSON body of the response, or undefined
    282     // if the response wasn't JSON.
    283     // May contain error details if 'status==0'
    284     body?: object;
    285   }