merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_helper.c (43630B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014--2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-merchant-httpd_helper.c
     18  * @brief shared logic for various handlers
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_db_lib.h>
     24 #include <taler/taler_json_lib.h>
     25 #include "taler-merchant-httpd_helper.h"
     26 #include <taler/taler_templating_lib.h>
     27 #include <taler/taler_dbevents.h>
     28 
     29 
     30 enum GNUNET_GenericReturnValue
     31 TMH_parse_fractional_string (const char *value,
     32                              int64_t *integer_part,
     33                              uint32_t *fractional_part)
     34 {
     35   const char *ptr;
     36   uint64_t integer = 0;
     37   uint32_t frac = 0;
     38   unsigned int digits = 0;
     39 
     40   GNUNET_assert (NULL != integer_part);
     41   GNUNET_assert (NULL != fractional_part);
     42 
     43   if (NULL == value)
     44   {
     45     GNUNET_break_op (0);
     46     return GNUNET_SYSERR;
     47   }
     48   ptr = value;
     49   if ('\0' == *ptr)
     50   {
     51     GNUNET_break_op (0);
     52     return GNUNET_SYSERR;
     53   }
     54   if ('-' == *ptr)
     55   {
     56     GNUNET_break_op (0);
     57     return GNUNET_SYSERR;
     58   }
     59   if (! isdigit ((unsigned char) *ptr))
     60   {
     61     GNUNET_break_op (0);
     62     return GNUNET_SYSERR;
     63   }
     64   while (isdigit ((unsigned char) *ptr))
     65   {
     66     unsigned int digit = (unsigned int) (*ptr - '0');
     67 
     68     if (integer > (UINT64_MAX - digit) / 10)
     69     {
     70       GNUNET_break_op (0);
     71       return GNUNET_SYSERR;
     72     }
     73     integer = integer * 10 + digit;
     74     ptr++;
     75   }
     76   if ('.' == *ptr)
     77   {
     78     ptr++;
     79     if ('\0' == *ptr)
     80     {
     81       GNUNET_break_op (0);
     82       return GNUNET_SYSERR;
     83     }
     84     while (isdigit ((unsigned char) *ptr))
     85     {
     86       unsigned int digit = (unsigned int) (*ptr - '0');
     87 
     88       if (digits >= MERCHANT_UNIT_FRAC_MAX_DIGITS)
     89       {
     90         GNUNET_break_op (0);
     91         return GNUNET_SYSERR;
     92       }
     93       frac = (uint32_t) (frac * 10 + digit);
     94       digits++;
     95       ptr++;
     96     }
     97     while (digits < MERCHANT_UNIT_FRAC_MAX_DIGITS)
     98     {
     99       frac *= 10;
    100       digits++;
    101     }
    102   }
    103   if ('\0' != *ptr)
    104   {
    105     GNUNET_break_op (0);
    106     return GNUNET_SYSERR;
    107   }
    108   if (integer > (uint64_t) INT64_MAX)
    109   {
    110     GNUNET_break_op (0);
    111     return GNUNET_SYSERR;
    112   }
    113   *integer_part = integer;
    114   *fractional_part = frac;
    115   return GNUNET_OK;
    116 }
    117 
    118 
    119 enum GNUNET_GenericReturnValue
    120 TMH_process_quantity_inputs (enum TMH_ValueKind kind,
    121                              bool allow_fractional,
    122                              bool int_missing,
    123                              int64_t int_raw,
    124                              bool str_missing,
    125                              const char *str_raw,
    126                              uint64_t *int_out,
    127                              uint32_t *frac_out,
    128                              const char **error_param)
    129 {
    130   static char errbuf[128];
    131   int64_t parsed_int = 0;
    132   uint32_t parsed_frac = 0;
    133   const char *int_field = (TMH_VK_STOCK == kind)
    134                           ? "total_stock"
    135                           : "quantity";
    136   const char *str_field = (TMH_VK_STOCK == kind)
    137                           ? "unit_total_stock"
    138                           : "unit_quantity";
    139 
    140   *error_param = NULL;
    141 
    142   if (int_missing && str_missing)
    143   {
    144     GNUNET_snprintf (errbuf,
    145                      sizeof (errbuf),
    146                      "missing %s and %s",
    147                      int_field,
    148                      str_field);
    149     *error_param = errbuf;
    150     GNUNET_break_op (0);
    151     return GNUNET_SYSERR;
    152   }
    153 
    154   if (! str_missing)
    155   {
    156     if ( (TMH_VK_STOCK == kind) &&
    157          (0 == strcmp ("-1",
    158                        str_raw)) )
    159     {
    160       parsed_int = -1;
    161       parsed_frac = 0;
    162     }
    163     else
    164     {
    165       if (GNUNET_OK !=
    166           TMH_parse_fractional_string (str_raw,
    167                                        &parsed_int,
    168                                        &parsed_frac))
    169       {
    170         GNUNET_snprintf (errbuf,
    171                          sizeof (errbuf),
    172                          "malformed %s",
    173                          str_field);
    174         *error_param = errbuf;
    175         GNUNET_break_op (0);
    176         return GNUNET_SYSERR;
    177       }
    178     }
    179   }
    180 
    181   if ( (! int_missing) && (! str_missing) )
    182   {
    183     if ( (parsed_int != int_raw) || (0 != parsed_frac) )
    184     {
    185       GNUNET_snprintf (errbuf,
    186                        sizeof (errbuf),
    187                        "%s/%s mismatch",
    188                        int_field,
    189                        str_field);
    190       *error_param = errbuf;
    191       GNUNET_break_op (0);
    192       return GNUNET_SYSERR;
    193     }
    194   }
    195   else if (int_missing)
    196   {
    197     int_raw = parsed_int;
    198   }
    199 
    200   if ( (TMH_VK_STOCK == kind) && (-1 == int_raw) )
    201   {
    202     if ( (! str_missing) && (0 != parsed_frac) )
    203     {
    204       GNUNET_snprintf (errbuf,
    205                        sizeof (errbuf),
    206                        "fractional part forbidden with %s='-1'",
    207                        str_field);
    208       *error_param = errbuf;
    209       GNUNET_break_op (0);
    210       return GNUNET_SYSERR;
    211     }
    212     *int_out = INT64_MAX;
    213     *frac_out = INT32_MAX;
    214     return GNUNET_OK;
    215   }
    216 
    217   if (int_raw < 0)
    218   {
    219     GNUNET_snprintf (errbuf,
    220                      sizeof (errbuf),
    221                      "%s must be non-negative",
    222                      int_field);
    223     *error_param = errbuf;
    224     GNUNET_break_op (0);
    225     return GNUNET_SYSERR;
    226   }
    227 
    228   if (! allow_fractional)
    229   {
    230     if ( (! str_missing) && (0 != parsed_frac) )
    231     {
    232       GNUNET_snprintf (errbuf,
    233                        sizeof (errbuf),
    234                        "fractional part not allowed for %s",
    235                        str_field);
    236       *error_param = errbuf;
    237       GNUNET_break_op (0);
    238       return GNUNET_SYSERR;
    239     }
    240     parsed_frac = 0;
    241   }
    242   else if (! str_missing)
    243   {
    244     if (parsed_frac >= MERCHANT_UNIT_FRAC_BASE)
    245     {
    246       GNUNET_snprintf (errbuf,
    247                        sizeof (errbuf),
    248                        "%s fractional part exceeds base %u",
    249                        str_field,
    250                        MERCHANT_UNIT_FRAC_BASE);
    251       *error_param = errbuf;
    252       GNUNET_break_op (0);
    253       return GNUNET_SYSERR;
    254     }
    255   }
    256 
    257   *int_out = (uint64_t) int_raw;
    258   *frac_out = parsed_frac;
    259   return GNUNET_OK;
    260 }
    261 
    262 
    263 void
    264 TMH_format_fractional_string (enum TMH_ValueKind kind,
    265                               uint64_t integer,
    266                               uint32_t fractional,
    267                               size_t buffer_length,
    268                               char buffer[static buffer_length])
    269 {
    270   GNUNET_assert (0 < buffer_length);
    271 
    272   if ( (TMH_VK_STOCK == kind) &&
    273        (INT64_MAX == (int64_t) integer) &&
    274        (INT32_MAX == (int32_t) fractional) )
    275   {
    276     GNUNET_snprintf (buffer,
    277                      buffer_length,
    278                      "-1");
    279     return;
    280   }
    281 
    282   GNUNET_assert ( (TMH_VK_QUANTITY != kind) ||
    283                   ((INT64_MAX != (int64_t) integer) &&
    284                    (INT32_MAX != (int32_t) fractional)) );
    285   GNUNET_assert (fractional < MERCHANT_UNIT_FRAC_BASE);
    286 
    287   if (0 == fractional)
    288   {
    289     GNUNET_snprintf (buffer,
    290                      buffer_length,
    291                      "%lu",
    292                      integer);
    293     return;
    294   }
    295   {
    296     char frac_buf[MERCHANT_UNIT_FRAC_MAX_DIGITS + 1];
    297     size_t idx;
    298 
    299     GNUNET_snprintf (frac_buf,
    300                      sizeof (frac_buf),
    301                      "%0*u",
    302                      MERCHANT_UNIT_FRAC_MAX_DIGITS,
    303                      (unsigned int) fractional);
    304     for (idx = strlen (frac_buf); idx > 0; idx--)
    305     {
    306       if ('0' != frac_buf[idx - 1])
    307         break;
    308       frac_buf[idx - 1] = '\0';
    309     }
    310     GNUNET_snprintf (buffer,
    311                      buffer_length,
    312                      "%lu.%s",
    313                      integer,
    314                      frac_buf);
    315   }
    316 }
    317 
    318 
    319 void
    320 TMH_quantity_defaults_from_unit (const struct TMH_MerchantInstance *mi,
    321                                  const char *unit,
    322                                  bool *allow_fractional,
    323                                  uint32_t *precision_level)
    324 {
    325   GNUNET_assert (NULL != allow_fractional);
    326   GNUNET_assert (NULL != precision_level);
    327   if (GNUNET_OK !=
    328       TMH_unit_defaults_for_instance (mi,
    329                                       unit,
    330                                       allow_fractional,
    331                                       precision_level))
    332   {
    333     *allow_fractional = false;
    334     *precision_level = 0;
    335   }
    336 }
    337 
    338 
    339 enum GNUNET_GenericReturnValue
    340 TMH_unit_defaults_for_instance (const struct TMH_MerchantInstance *mi,
    341                                 const char *unit,
    342                                 bool *allow_fractional,
    343                                 uint32_t *precision_level)
    344 {
    345   struct TALER_MERCHANTDB_UnitDetails ud = { 0 };
    346   enum GNUNET_DB_QueryStatus qs;
    347   bool allow = false;
    348   uint32_t precision = 0;
    349 
    350   GNUNET_assert (NULL != allow_fractional);
    351   GNUNET_assert (NULL != precision_level);
    352 
    353   qs = TMH_db->select_unit (TMH_db->cls,
    354                             mi->settings.id,
    355                             unit,
    356                             &ud);
    357   switch (qs)
    358   {
    359   case GNUNET_DB_STATUS_HARD_ERROR:
    360   case GNUNET_DB_STATUS_SOFT_ERROR:
    361     TALER_MERCHANTDB_unit_details_free (&ud);
    362     return GNUNET_SYSERR;
    363   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    364     allow = ud.unit_allow_fraction;
    365     precision = ud.unit_precision_level;
    366     break;
    367   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    368     break;
    369   default:
    370     GNUNET_break (0);
    371     TALER_MERCHANTDB_unit_details_free (&ud);
    372     return GNUNET_SYSERR;
    373   }
    374   TALER_MERCHANTDB_unit_details_free (&ud);
    375 
    376   /* This is definitely not supposed to happen
    377      combination of allow -> false, and precision > 0
    378      yet let's fix it */
    379   if (! allow)
    380   {
    381     GNUNET_break (0);
    382     precision = 0;
    383   }
    384   *allow_fractional = allow;
    385   *precision_level = precision;
    386   return GNUNET_OK;
    387 }
    388 
    389 
    390 enum GNUNET_GenericReturnValue
    391 TMH_cmp_wire_account (
    392   const json_t *account,
    393   const struct TMH_WireMethod *wm)
    394 {
    395   const char *credit_facade_url = NULL;
    396   const json_t *credit_facade_credentials = NULL;
    397   struct TALER_FullPayto uri;
    398   struct GNUNET_JSON_Specification ispec[] = {
    399     TALER_JSON_spec_full_payto_uri ("payto_uri",
    400                                     &uri),
    401     GNUNET_JSON_spec_mark_optional (
    402       TALER_JSON_spec_web_url ("credit_facade_url",
    403                                &credit_facade_url),
    404       NULL),
    405     GNUNET_JSON_spec_mark_optional (
    406       GNUNET_JSON_spec_object_const ("credit_facade_credentials",
    407                                      &credit_facade_credentials),
    408       NULL),
    409     GNUNET_JSON_spec_end ()
    410   };
    411   enum GNUNET_GenericReturnValue res;
    412   const char *ename;
    413   unsigned int eline;
    414 
    415   res = GNUNET_JSON_parse (account,
    416                            ispec,
    417                            &ename,
    418                            &eline);
    419   if (GNUNET_OK != res)
    420   {
    421     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    422                 "Failed to parse account spec: %s (%u)\n",
    423                 ename,
    424                 eline);
    425     return GNUNET_SYSERR;
    426   }
    427   if (0 !=
    428       TALER_full_payto_cmp (wm->payto_uri,
    429                             uri))
    430   {
    431     return GNUNET_SYSERR;
    432   }
    433   if ( (NULL == credit_facade_url) !=
    434        (NULL == wm->credit_facade_url) ||
    435        (NULL == credit_facade_credentials) !=
    436        (NULL == wm->credit_facade_credentials) )
    437   {
    438     return GNUNET_NO;
    439   }
    440   if ( (NULL != credit_facade_url) &&
    441        (0 != strcmp (credit_facade_url,
    442                      wm->credit_facade_url)) )
    443   {
    444     return GNUNET_NO;
    445   }
    446   if ( (NULL != credit_facade_credentials) &&
    447        (0 != json_equal (credit_facade_credentials,
    448                          wm->credit_facade_credentials)) )
    449   {
    450     return GNUNET_NO;
    451   }
    452   return GNUNET_YES;
    453 }
    454 
    455 
    456 bool
    457 TMH_accounts_array_valid (const json_t *accounts)
    458 {
    459   size_t len;
    460 
    461   if (! json_is_array (accounts))
    462   {
    463     GNUNET_break_op (0);
    464     return false;
    465   }
    466   len = json_array_size (accounts);
    467   for (size_t i = 0; i<len; i++)
    468   {
    469     json_t *payto_uri = json_array_get (accounts,
    470                                         i);
    471     const char *credit_facade_url = NULL;
    472     const json_t *credit_facade_credentials = NULL;
    473     struct TALER_FullPayto uri;
    474     struct GNUNET_JSON_Specification ispec[] = {
    475       TALER_JSON_spec_full_payto_uri ("payto_uri",
    476                                       &uri),
    477       GNUNET_JSON_spec_mark_optional (
    478         TALER_JSON_spec_web_url ("credit_facade_url",
    479                                  &credit_facade_url),
    480         NULL),
    481       GNUNET_JSON_spec_mark_optional (
    482         GNUNET_JSON_spec_object_const ("credit_facade_credentials",
    483                                        &credit_facade_credentials),
    484         NULL),
    485       GNUNET_JSON_spec_end ()
    486     };
    487     enum GNUNET_GenericReturnValue res;
    488     const char *ename;
    489     unsigned int eline;
    490 
    491     res = GNUNET_JSON_parse (payto_uri,
    492                              ispec,
    493                              &ename,
    494                              &eline);
    495     if (GNUNET_OK != res)
    496     {
    497       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    498                   "Failed to parse account spec: %s (%u)\n",
    499                   ename,
    500                   eline);
    501       return false;
    502     }
    503 
    504     /* Test for the same payto:// URI being given twice */
    505     for (size_t j = 0; j<i; j++)
    506     {
    507       json_t *old_uri = json_array_get (accounts,
    508                                         j);
    509       if (0 == strcmp (uri.full_payto,
    510                        json_string_value (
    511                          json_object_get (old_uri,
    512                                           "payto_uri"))))
    513       {
    514         GNUNET_break_op (0);
    515         return false;
    516       }
    517     }
    518     {
    519       char *err;
    520 
    521       if (NULL !=
    522           (err = TALER_payto_validate (uri)))
    523       {
    524         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    525                     "Encountered invalid payto://-URI `%s': %s\n",
    526                     uri.full_payto,
    527                     err);
    528         GNUNET_free (err);
    529         return false;
    530       }
    531     }
    532     if ( (NULL == credit_facade_url) !=
    533          (NULL == credit_facade_credentials) )
    534     {
    535       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    536                   "If credit_facade_url is given, credit_facade_credentials must also be specified (violated for %s)\n",
    537                   uri.full_payto);
    538       return false;
    539     }
    540     if ( (NULL != credit_facade_url) ||
    541          (NULL != credit_facade_credentials) )
    542     {
    543       struct TALER_MERCHANT_BANK_AuthenticationData auth;
    544 
    545       if (GNUNET_OK !=
    546           TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
    547                                                credit_facade_url,
    548                                                &auth))
    549       {
    550         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    551                     "Invalid credit facade URL or credentials `%s'\n",
    552                     credit_facade_url);
    553         return false;
    554       }
    555       TALER_MERCHANT_BANK_auth_free (&auth);
    556     }
    557   } /* end for all accounts */
    558   return true;
    559 }
    560 
    561 
    562 bool
    563 TMH_location_object_valid (const json_t *location)
    564 {
    565   const char *country = NULL;
    566   const char *subdivision = NULL;
    567   const char *district = NULL;
    568   const char *town = NULL;
    569   const char *town_loc = NULL;
    570   const char *postcode = NULL;
    571   const char *street = NULL;
    572   const char *building = NULL;
    573   const char *building_no = NULL;
    574   const json_t *lines = NULL;
    575   struct GNUNET_JSON_Specification spec[] = {
    576     GNUNET_JSON_spec_mark_optional (
    577       GNUNET_JSON_spec_string ("country",
    578                                &country),
    579       NULL),
    580     GNUNET_JSON_spec_mark_optional (
    581       GNUNET_JSON_spec_string ("country_subdivision",
    582                                &subdivision),
    583       NULL),
    584     GNUNET_JSON_spec_mark_optional (
    585       GNUNET_JSON_spec_string ("district",
    586                                &district),
    587       NULL),
    588     GNUNET_JSON_spec_mark_optional (
    589       GNUNET_JSON_spec_string ("town",
    590                                &town),
    591       NULL),
    592     GNUNET_JSON_spec_mark_optional (
    593       GNUNET_JSON_spec_string ("town_location",
    594                                &town_loc),
    595       NULL),
    596     GNUNET_JSON_spec_mark_optional (
    597       GNUNET_JSON_spec_string ("post_code",
    598                                &postcode),
    599       NULL),
    600     GNUNET_JSON_spec_mark_optional (
    601       GNUNET_JSON_spec_string ("street",
    602                                &street),
    603       NULL),
    604     GNUNET_JSON_spec_mark_optional (
    605       GNUNET_JSON_spec_string ("building_name",
    606                                &building),
    607       NULL),
    608     GNUNET_JSON_spec_mark_optional (
    609       GNUNET_JSON_spec_string ("building_number",
    610                                &building_no),
    611       NULL),
    612     GNUNET_JSON_spec_mark_optional (
    613       GNUNET_JSON_spec_array_const ("address_lines",
    614                                     &lines),
    615       NULL),
    616     GNUNET_JSON_spec_end ()
    617   };
    618   const char *ename;
    619   unsigned int eline;
    620 
    621   if (GNUNET_OK !=
    622       GNUNET_JSON_parse (location,
    623                          spec,
    624                          &ename,
    625                          &eline))
    626   {
    627     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    628                 "Invalid location for field %s\n",
    629                 ename);
    630     return false;
    631   }
    632   if (NULL != lines)
    633   {
    634     size_t idx;
    635     json_t *line;
    636 
    637     json_array_foreach (lines, idx, line)
    638     {
    639       if (! json_is_string (line))
    640       {
    641         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    642                     "Invalid address line #%u in location\n",
    643                     (unsigned int) idx);
    644         return false;
    645       }
    646     }
    647   }
    648   return true;
    649 }
    650 
    651 
    652 bool
    653 TMH_products_array_valid (const json_t *products)
    654 {
    655   const json_t *product;
    656   size_t idx;
    657   bool valid = true;
    658 
    659   if (! json_is_array (products))
    660   {
    661     GNUNET_break_op (0);
    662     return false;
    663   }
    664   json_array_foreach ((json_t *) products, idx, product)
    665   {
    666     const char *product_id = NULL;
    667     const char *description;
    668     uint64_t quantity = 0;
    669     bool quantity_missing = true;
    670     const char *unit_quantity = NULL;
    671     bool unit_quantity_missing = true;
    672     const char *unit = NULL;
    673     struct TALER_Amount price = { .value = 0 };
    674     const char *image_data_url = NULL;
    675     const json_t *taxes = NULL;
    676     struct GNUNET_TIME_Timestamp delivery_date = { 0 };
    677     struct GNUNET_JSON_Specification spec[] = {
    678       GNUNET_JSON_spec_mark_optional (
    679         GNUNET_JSON_spec_string ("product_id",
    680                                  &product_id),
    681         NULL),
    682       TALER_JSON_spec_i18n_str ("description",
    683                                 &description),
    684       GNUNET_JSON_spec_mark_optional (
    685         GNUNET_JSON_spec_uint64 ("quantity",
    686                                  &quantity),
    687         &quantity_missing),
    688       GNUNET_JSON_spec_mark_optional (
    689         GNUNET_JSON_spec_string ("unit_quantity",
    690                                  &unit_quantity),
    691         &unit_quantity_missing),
    692       GNUNET_JSON_spec_mark_optional (
    693         GNUNET_JSON_spec_string ("unit",
    694                                  &unit),
    695         NULL),
    696       GNUNET_JSON_spec_mark_optional (
    697         TALER_JSON_spec_amount_any ("price",
    698                                     &price),
    699         NULL),
    700       GNUNET_JSON_spec_mark_optional (
    701         GNUNET_JSON_spec_string ("image",
    702                                  &image_data_url),
    703         NULL),
    704       GNUNET_JSON_spec_mark_optional (
    705         GNUNET_JSON_spec_array_const ("taxes",
    706                                       &taxes),
    707         NULL),
    708       GNUNET_JSON_spec_mark_optional (
    709         GNUNET_JSON_spec_timestamp ("delivery_date",
    710                                     &delivery_date),
    711         NULL),
    712       GNUNET_JSON_spec_end ()
    713     };
    714     const char *ename;
    715     unsigned int eline;
    716 
    717     if (GNUNET_OK !=
    718         GNUNET_JSON_parse (product,
    719                            spec,
    720                            &ename,
    721                            &eline))
    722     {
    723       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    724                   "Invalid product #%u for field %s\n",
    725                   (unsigned int) idx,
    726                   ename);
    727       return false;
    728     }
    729     if (quantity_missing)
    730     {
    731       if (unit_quantity_missing)
    732       {
    733         GNUNET_break_op (0);
    734         valid = false;
    735       }
    736     }
    737     if ( (NULL != image_data_url) &&
    738          (! TMH_image_data_url_valid (image_data_url)) )
    739     {
    740       GNUNET_break_op (0);
    741       valid = false;
    742     }
    743     if ( (NULL != taxes) &&
    744          (! TMH_taxes_array_valid (taxes)) )
    745     {
    746       GNUNET_break_op (0);
    747       valid = false;
    748     }
    749     GNUNET_JSON_parse_free (spec);
    750     if (! valid)
    751       break;
    752   }
    753 
    754   return valid;
    755 }
    756 
    757 
    758 bool
    759 TMH_image_data_url_valid (const char *image_data_url)
    760 {
    761   if (0 == strcmp (image_data_url,
    762                    ""))
    763     return true;
    764   if (0 != strncasecmp ("data:image/",
    765                         image_data_url,
    766                         strlen ("data:image/")))
    767   {
    768     GNUNET_break_op (0);
    769     return false;
    770   }
    771   if (NULL == strstr (image_data_url,
    772                       ";base64,"))
    773   {
    774     GNUNET_break_op (0);
    775     return false;
    776   }
    777   if (! TALER_url_valid_charset (image_data_url))
    778   {
    779     GNUNET_break_op (0);
    780     return false;
    781   }
    782   return true;
    783 }
    784 
    785 
    786 bool
    787 TMH_template_contract_valid (const json_t *template_contract)
    788 {
    789   const char *summary;
    790   const char *currency;
    791   struct TALER_Amount amount = { .value = 0};
    792   uint32_t minimum_age = 0;
    793   struct GNUNET_TIME_Relative pay_duration = { 0 };
    794   struct GNUNET_JSON_Specification spec[] = {
    795     GNUNET_JSON_spec_mark_optional (
    796       GNUNET_JSON_spec_string ("summary",
    797                                &summary),
    798       NULL),
    799     GNUNET_JSON_spec_mark_optional (
    800       GNUNET_JSON_spec_string ("currency",
    801                                &currency),
    802       NULL),
    803     GNUNET_JSON_spec_mark_optional (
    804       TALER_JSON_spec_amount_any ("amount",
    805                                   &amount),
    806       NULL),
    807     GNUNET_JSON_spec_uint32 ("minimum_age",
    808                              &minimum_age),
    809     GNUNET_JSON_spec_relative_time ("pay_duration",
    810                                     &pay_duration),
    811     GNUNET_JSON_spec_end ()
    812   };
    813   const char *ename;
    814   unsigned int eline;
    815 
    816   if (GNUNET_OK !=
    817       GNUNET_JSON_parse (template_contract,
    818                          spec,
    819                          &ename,
    820                          &eline))
    821   {
    822     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    823                 "Invalid template_contract for field %s\n",
    824                 ename);
    825     return false;
    826   }
    827   return true;
    828 }
    829 
    830 
    831 bool
    832 TMH_taxes_array_valid (const json_t *taxes)
    833 {
    834   json_t *tax;
    835   size_t idx;
    836 
    837   if (! json_is_array (taxes))
    838     return false;
    839   json_array_foreach (taxes, idx, tax)
    840   {
    841     struct TALER_Amount amount;
    842     const char *name;
    843     struct GNUNET_JSON_Specification spec[] = {
    844       GNUNET_JSON_spec_string ("name",
    845                                &name),
    846       TALER_JSON_spec_amount_any ("tax",
    847                                   &amount),
    848       GNUNET_JSON_spec_end ()
    849     };
    850     enum GNUNET_GenericReturnValue res;
    851 
    852     res = TALER_MHD_parse_json_data (NULL,
    853                                      tax,
    854                                      spec);
    855     if (GNUNET_OK != res)
    856     {
    857       GNUNET_break_op (0);
    858       return false;
    859     }
    860   }
    861   return true;
    862 }
    863 
    864 
    865 struct TMH_WireMethod *
    866 TMH_setup_wire_account (
    867   struct TALER_FullPayto payto_uri,
    868   const char *credit_facade_url,
    869   const json_t *credit_facade_credentials)
    870 {
    871   struct TMH_WireMethod *wm;
    872   char *emsg;
    873 
    874   if (NULL != (emsg = TALER_payto_validate (payto_uri)))
    875   {
    876     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    877                 "Invalid URI `%s': %s\n",
    878                 payto_uri.full_payto,
    879                 emsg);
    880     GNUNET_free (emsg);
    881     return NULL;
    882   }
    883 
    884   wm = GNUNET_new (struct TMH_WireMethod);
    885   if (NULL != credit_facade_url)
    886     wm->credit_facade_url
    887       = GNUNET_strdup (credit_facade_url);
    888   if (NULL != credit_facade_credentials)
    889     wm->credit_facade_credentials
    890       = json_incref ((json_t*) credit_facade_credentials);
    891   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    892                               &wm->wire_salt,
    893                               sizeof (wm->wire_salt));
    894   wm->payto_uri.full_payto
    895     = GNUNET_strdup (payto_uri.full_payto);
    896   TALER_merchant_wire_signature_hash (payto_uri,
    897                                       &wm->wire_salt,
    898                                       &wm->h_wire);
    899   wm->wire_method
    900     = TALER_payto_get_method (payto_uri.full_payto);
    901   wm->active = true;
    902   return wm;
    903 }
    904 
    905 
    906 enum TALER_ErrorCode
    907 TMH_check_token (const char *token,
    908                  const char *instance_id,
    909                  enum TMH_AuthScope *as)
    910 {
    911   enum TMH_AuthScope scope;
    912   struct GNUNET_TIME_Timestamp expiration;
    913   enum GNUNET_DB_QueryStatus qs;
    914   struct TALER_MERCHANTDB_LoginTokenP btoken;
    915 
    916   if (NULL == token)
    917   {
    918     *as = TMH_AS_NONE;
    919     return TALER_EC_NONE;
    920   }
    921   if (0 != strncasecmp (token,
    922                         RFC_8959_PREFIX,
    923                         strlen (RFC_8959_PREFIX)))
    924   {
    925     *as = TMH_AS_NONE;
    926     return TALER_EC_NONE;
    927   }
    928   token += strlen (RFC_8959_PREFIX);
    929   if (GNUNET_OK !=
    930       GNUNET_STRINGS_string_to_data (token,
    931                                      strlen (token),
    932                                      &btoken,
    933                                      sizeof (btoken)))
    934   {
    935     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    936                 "Given authorization token `%s' is malformed\n",
    937                 token);
    938     GNUNET_break_op (0);
    939     return TALER_EC_GENERIC_TOKEN_MALFORMED;
    940   }
    941   qs = TMH_db->select_login_token (TMH_db->cls,
    942                                    instance_id,
    943                                    &btoken,
    944                                    &expiration,
    945                                    (uint32_t*) &scope);
    946   if (qs < 0)
    947   {
    948     GNUNET_break (0);
    949     return TALER_EC_GENERIC_DB_FETCH_FAILED;
    950   }
    951   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    952   {
    953     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    954                 "Authorization token `%s' unknown\n",
    955                 token);
    956     return TALER_EC_GENERIC_TOKEN_UNKNOWN;
    957   }
    958   if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
    959   {
    960     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    961                 "Authorization token `%s' expired\n",
    962                 token);
    963     return TALER_EC_GENERIC_TOKEN_EXPIRED;
    964   }
    965   *as = scope;
    966   return TALER_EC_NONE;
    967 }
    968 
    969 
    970 enum GNUNET_GenericReturnValue
    971 TMH_check_auth_config (struct MHD_Connection *connection,
    972                        const json_t *jauth,
    973                        const char **auth_password)
    974 {
    975   bool auth_wellformed = false;
    976   const char *auth_method = json_string_value (json_object_get (jauth,
    977                                                                 "method"));
    978 
    979   *auth_password = NULL;
    980   if (NULL == auth_method)
    981   {
    982     GNUNET_break_op (0);
    983   }
    984   else if ((GNUNET_YES != TMH_strict_v19) &&
    985            (0 == strcmp (auth_method,
    986                          "external")))
    987   {
    988     auth_wellformed = true;
    989   }
    990   else if (GNUNET_YES == TMH_auth_disabled)
    991   {
    992     auth_wellformed = true;
    993   }
    994   else if (0 == strcmp (auth_method,
    995                         "token"))
    996   {
    997     json_t *pw_value;
    998 
    999     pw_value = json_object_get (jauth,
   1000                                 "password");
   1001     if (NULL == pw_value)
   1002     {
   1003       pw_value = json_object_get (jauth,
   1004                                   "token");
   1005     }
   1006     if (NULL == pw_value)
   1007     {
   1008       auth_wellformed = false;
   1009       GNUNET_break_op (0);
   1010     }
   1011     else
   1012     {
   1013       *auth_password = json_string_value (pw_value);
   1014       if (NULL != *auth_password)
   1015       {
   1016         if (0 == strncasecmp (RFC_8959_PREFIX,
   1017                               *auth_password,
   1018                               strlen (RFC_8959_PREFIX)))
   1019         {
   1020           *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
   1021         }
   1022         auth_wellformed = true;
   1023       }
   1024     }
   1025   }
   1026 
   1027   if (! auth_wellformed)
   1028   {
   1029     GNUNET_break_op (0);
   1030     return (MHD_YES ==
   1031             TALER_MHD_reply_with_error (connection,
   1032                                         MHD_HTTP_BAD_REQUEST,
   1033                                         TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
   1034                                         "bad authentication config"))
   1035       ? GNUNET_NO
   1036       : GNUNET_SYSERR;
   1037   }
   1038   return GNUNET_OK;
   1039 }
   1040 
   1041 
   1042 void
   1043 TMH_uuid_from_string (const char *uuids,
   1044                       struct GNUNET_Uuid *uuid)
   1045 {
   1046   struct GNUNET_HashCode hc;
   1047 
   1048   GNUNET_CRYPTO_hash (uuids,
   1049                       strlen (uuids),
   1050                       &hc);
   1051   GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
   1052   GNUNET_memcpy (uuid,
   1053                  &hc,
   1054                  sizeof (*uuid));
   1055 }
   1056 
   1057 
   1058 /**
   1059  * Closure for #trigger_webhook_cb.
   1060  *
   1061  * @param instance which is the instance we work with
   1062  * @param root JSON data to fill into the template
   1063  * @param rv, query of the TALER_TEMPLATEING_fill
   1064  */
   1065 struct Trigger
   1066 {
   1067   const char *instance;
   1068 
   1069   const json_t *root;
   1070 
   1071   enum GNUNET_DB_QueryStatus rv;
   1072 
   1073 };
   1074 
   1075 /**
   1076  * Typically called by `TMH_trigger_webhook`.
   1077  *
   1078  * @param[in,out] cls a `struct Trigger` with information about the webhook
   1079  * @param webhook_serial reference to the configured webhook template.
   1080  * @param event_type is the event/action of the webhook
   1081  * @param url to make request to
   1082  * @param http_method use for the webhook
   1083  * @param header_template of the webhook
   1084  * @param body_template of the webhook
   1085  */
   1086 static void
   1087 trigger_webhook_cb (void *cls,
   1088                     uint64_t webhook_serial,
   1089                     const char *event_type,
   1090                     const char *url,
   1091                     const char *http_method,
   1092                     const char *header_template,
   1093                     const char *body_template)
   1094 {
   1095   struct Trigger *t = cls;
   1096   void *header = NULL;
   1097   void *body = NULL;
   1098   size_t header_size;
   1099   size_t body_size;
   1100 
   1101   if (NULL != header_template)
   1102   {
   1103     int ret;
   1104 
   1105     ret = TALER_TEMPLATING_fill (header_template,
   1106                                  t->root,
   1107                                  &header,
   1108                                  &header_size);
   1109     if (0 != ret)
   1110     {
   1111       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1112                   "Failed to expand webhook header template for webhook %llu (%d)\n",
   1113                   (unsigned long long) webhook_serial,
   1114                   ret);
   1115       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
   1116       return;
   1117     }
   1118     /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
   1119     GNUNET_assert ('\0' == ((const char *) header)[header_size]);
   1120   }
   1121   if (NULL != body_template)
   1122   {
   1123     int ret;
   1124     ret = TALER_TEMPLATING_fill (body_template,
   1125                                  t->root,
   1126                                  &body,
   1127                                  &body_size);
   1128     if (0 != ret)
   1129     {
   1130       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1131                   "Failed to expand webhook body template for webhook %llu (%d)\n",
   1132                   (unsigned long long) webhook_serial,
   1133                   ret);
   1134       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
   1135       return;
   1136     }
   1137     /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
   1138     GNUNET_assert ('\0' == ((const char *) body)[body_size]);
   1139   }
   1140   t->rv = TMH_db->insert_pending_webhook (TMH_db->cls,
   1141                                           t->instance,
   1142                                           webhook_serial,
   1143                                           url,
   1144                                           http_method,
   1145                                           header,
   1146                                           body);
   1147   if (t->rv > 0)
   1148   {
   1149     struct GNUNET_DB_EventHeaderP es = {
   1150       .size = htons (sizeof(es)),
   1151       .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
   1152     };
   1153     const void *extra = NULL;
   1154     size_t extra_size = 0;
   1155     TMH_db->event_notify (TMH_db->cls,
   1156                           &es,
   1157                           &extra,
   1158                           extra_size);
   1159   }
   1160   free (header);
   1161   free (body);
   1162 }
   1163 
   1164 
   1165 /**
   1166  * TMH_trigger_webhook is a function that need to be use when someone
   1167  * pay. Merchant need to have a notification.
   1168  *
   1169  * @param instance that we need to send the webhook as a notification
   1170  * @param event of the webhook
   1171  * @param args argument of the function
   1172  */
   1173 enum GNUNET_DB_QueryStatus
   1174 TMH_trigger_webhook (const char *instance,
   1175                      const char *event,
   1176                      const json_t *args)
   1177 {
   1178   struct Trigger t = {
   1179     .instance = instance,
   1180     .root = args
   1181   };
   1182   enum GNUNET_DB_QueryStatus qs;
   1183 
   1184   qs = TMH_db->lookup_webhook_by_event (TMH_db->cls,
   1185                                         instance,
   1186                                         event,
   1187                                         &trigger_webhook_cb,
   1188                                         &t);
   1189   if (qs < 0)
   1190     return qs;
   1191   return t.rv;
   1192 }
   1193 
   1194 
   1195 enum GNUNET_GenericReturnValue
   1196 TMH_base_url_by_connection (struct MHD_Connection *connection,
   1197                             const char *instance,
   1198                             struct GNUNET_Buffer *buf)
   1199 {
   1200   const char *host;
   1201   const char *forwarded_host;
   1202   const char *forwarded_port;
   1203   const char *uri_path;
   1204 
   1205   memset (buf,
   1206           0,
   1207           sizeof (*buf));
   1208   if (NULL != TMH_base_url)
   1209   {
   1210     GNUNET_buffer_write_str (buf,
   1211                              TMH_base_url);
   1212   }
   1213   else
   1214   {
   1215     if (GNUNET_YES ==
   1216         TALER_mhd_is_https (connection))
   1217       GNUNET_buffer_write_str (buf,
   1218                                "https://");
   1219     else
   1220       GNUNET_buffer_write_str (buf,
   1221                                "http://");
   1222     host = MHD_lookup_connection_value (connection,
   1223                                         MHD_HEADER_KIND,
   1224                                         MHD_HTTP_HEADER_HOST);
   1225     forwarded_host = MHD_lookup_connection_value (connection,
   1226                                                   MHD_HEADER_KIND,
   1227                                                   "X-Forwarded-Host");
   1228     if (NULL != forwarded_host)
   1229     {
   1230       GNUNET_buffer_write_str (buf,
   1231                                forwarded_host);
   1232     }
   1233     else
   1234     {
   1235       if (NULL == host)
   1236       {
   1237         GNUNET_buffer_clear (buf);
   1238         GNUNET_break (0);
   1239         return GNUNET_SYSERR;
   1240       }
   1241       GNUNET_buffer_write_str (buf,
   1242                                host);
   1243     }
   1244     forwarded_port = MHD_lookup_connection_value (connection,
   1245                                                   MHD_HEADER_KIND,
   1246                                                   "X-Forwarded-Port");
   1247     if (NULL != forwarded_port)
   1248     {
   1249       GNUNET_buffer_write_str (buf,
   1250                                ":");
   1251       GNUNET_buffer_write_str (buf,
   1252                                forwarded_port);
   1253     }
   1254     uri_path = MHD_lookup_connection_value (connection,
   1255                                             MHD_HEADER_KIND,
   1256                                             "X-Forwarded-Prefix");
   1257     if (NULL != uri_path)
   1258       GNUNET_buffer_write_path (buf,
   1259                                 uri_path);
   1260   }
   1261   if (0 != strcmp (instance,
   1262                    "admin"))
   1263   {
   1264     GNUNET_buffer_write_path (buf,
   1265                               "/instances/");
   1266     GNUNET_buffer_write_str (buf,
   1267                              instance);
   1268   }
   1269   return GNUNET_OK;
   1270 }
   1271 
   1272 
   1273 enum GNUNET_GenericReturnValue
   1274 TMH_taler_uri_by_connection (struct MHD_Connection *connection,
   1275                              const char *method,
   1276                              const char *instance,
   1277                              struct GNUNET_Buffer *buf)
   1278 {
   1279   const char *host;
   1280   const char *forwarded_host;
   1281   const char *forwarded_port;
   1282   const char *uri_path;
   1283 
   1284   memset (buf,
   1285           0,
   1286           sizeof (*buf));
   1287   host = MHD_lookup_connection_value (connection,
   1288                                       MHD_HEADER_KIND,
   1289                                       "Host");
   1290   forwarded_host = MHD_lookup_connection_value (connection,
   1291                                                 MHD_HEADER_KIND,
   1292                                                 "X-Forwarded-Host");
   1293   forwarded_port = MHD_lookup_connection_value (connection,
   1294                                                 MHD_HEADER_KIND,
   1295                                                 "X-Forwarded-Port");
   1296   uri_path = MHD_lookup_connection_value (connection,
   1297                                           MHD_HEADER_KIND,
   1298                                           "X-Forwarded-Prefix");
   1299   if (NULL != forwarded_host)
   1300     host = forwarded_host;
   1301   if (NULL == host)
   1302   {
   1303     GNUNET_break (0);
   1304     return GNUNET_SYSERR;
   1305   }
   1306   GNUNET_buffer_write_str (buf,
   1307                            "taler");
   1308   if (GNUNET_NO == TALER_mhd_is_https (connection))
   1309     GNUNET_buffer_write_str (buf,
   1310                              "+http");
   1311   GNUNET_buffer_write_str (buf,
   1312                            "://");
   1313   GNUNET_buffer_write_str (buf,
   1314                            method);
   1315   GNUNET_buffer_write_str (buf,
   1316                            "/");
   1317   GNUNET_buffer_write_str (buf,
   1318                            host);
   1319   if (NULL != forwarded_port)
   1320   {
   1321     GNUNET_buffer_write_str (buf,
   1322                              ":");
   1323     GNUNET_buffer_write_str (buf,
   1324                              forwarded_port);
   1325   }
   1326   if (NULL != uri_path)
   1327     GNUNET_buffer_write_path (buf,
   1328                               uri_path);
   1329   if (0 != strcmp ("admin",
   1330                    instance))
   1331   {
   1332     GNUNET_buffer_write_path (buf,
   1333                               "instances");
   1334     GNUNET_buffer_write_path (buf,
   1335                               instance);
   1336   }
   1337   return GNUNET_OK;
   1338 }
   1339 
   1340 
   1341 /**
   1342  * Closure for #add_matching_account().
   1343  */
   1344 struct ExchangeMatchContext
   1345 {
   1346   /**
   1347    * Wire method to match, NULL for all.
   1348    */
   1349   const char *wire_method;
   1350 
   1351   /**
   1352    * Array of accounts to return.
   1353    */
   1354   json_t *accounts;
   1355 };
   1356 
   1357 
   1358 /**
   1359  * If the given account is feasible, add it to the array
   1360  * of accounts we return.
   1361  *
   1362  * @param cls a `struct PostReserveContext`
   1363  * @param payto_uri URI of the account
   1364  * @param conversion_url URL of a conversion service
   1365  * @param debit_restrictions restrictions for debits from account
   1366  * @param credit_restrictions restrictions for credits to account
   1367  * @param master_sig signature affirming the account
   1368  */
   1369 static void
   1370 add_matching_account (
   1371   void *cls,
   1372   struct TALER_FullPayto payto_uri,
   1373   const char *conversion_url,
   1374   const json_t *debit_restrictions,
   1375   const json_t *credit_restrictions,
   1376   const struct TALER_MasterSignatureP *master_sig)
   1377 {
   1378   struct ExchangeMatchContext *rc = cls;
   1379   char *method;
   1380 
   1381   method = TALER_payto_get_method (payto_uri.full_payto);
   1382   if ( (NULL == rc->wire_method) ||
   1383        (0 == strcmp (method,
   1384                      rc->wire_method)) )
   1385   {
   1386     json_t *acc;
   1387 
   1388     acc = GNUNET_JSON_PACK (
   1389       TALER_JSON_pack_full_payto ("payto_uri",
   1390                                   payto_uri),
   1391       GNUNET_JSON_pack_data_auto ("master_sig",
   1392                                   master_sig),
   1393       GNUNET_JSON_pack_allow_null (
   1394         GNUNET_JSON_pack_string ("conversion_url",
   1395                                  conversion_url)),
   1396       GNUNET_JSON_pack_array_incref ("credit_restrictions",
   1397                                      (json_t *) credit_restrictions),
   1398       GNUNET_JSON_pack_array_incref ("debit_restrictions",
   1399                                      (json_t *) debit_restrictions)
   1400       );
   1401     GNUNET_assert (0 ==
   1402                    json_array_append_new (rc->accounts,
   1403                                           acc));
   1404   }
   1405   GNUNET_free (method);
   1406 }
   1407 
   1408 
   1409 /**
   1410  * Return JSON array with all of the exchange accounts
   1411  * that support the given @a wire_method.
   1412  *
   1413  * @param master_pub master public key to match exchange by
   1414  * @param wire_method NULL for any
   1415  * @return JSON array with information about all matching accounts
   1416  */
   1417 json_t *
   1418 TMH_exchange_accounts_by_method (
   1419   const struct TALER_MasterPublicKeyP *master_pub,
   1420   const char *wire_method)
   1421 {
   1422   struct ExchangeMatchContext emc = {
   1423     .wire_method = wire_method,
   1424     .accounts = json_array ()
   1425   };
   1426   enum GNUNET_DB_QueryStatus qs;
   1427 
   1428   GNUNET_assert (NULL != emc.accounts);
   1429   qs = TMH_db->select_accounts_by_exchange (TMH_db->cls,
   1430                                             master_pub,
   1431                                             &add_matching_account,
   1432                                             &emc);
   1433   if (qs < 0)
   1434   {
   1435     json_decref (emc.accounts);
   1436     return NULL;
   1437   }
   1438   return emc.accounts;
   1439 }
   1440 
   1441 
   1442 char *
   1443 TMH_make_order_status_url (struct MHD_Connection *con,
   1444                            const char *order_id,
   1445                            const char *session_id,
   1446                            const char *instance_id,
   1447                            struct TALER_ClaimTokenP *claim_token,
   1448                            struct TALER_PrivateContractHashP *h_contract)
   1449 {
   1450   struct GNUNET_Buffer buf;
   1451   /* Number of query parameters written so far */
   1452   unsigned int num_qp = 0;
   1453 
   1454   GNUNET_assert (NULL != instance_id);
   1455   GNUNET_assert (NULL != order_id);
   1456   if (GNUNET_OK !=
   1457       TMH_base_url_by_connection (con,
   1458                                   instance_id,
   1459                                   &buf))
   1460   {
   1461     GNUNET_break (0);
   1462     return NULL;
   1463   }
   1464   GNUNET_buffer_write_path (&buf,
   1465                             "/orders");
   1466   GNUNET_buffer_write_path (&buf,
   1467                             order_id);
   1468   if ( (NULL != claim_token) &&
   1469        (! GNUNET_is_zero (claim_token)) )
   1470   {
   1471     /* 'token=' for human readability */
   1472     GNUNET_buffer_write_str (&buf,
   1473                              "?token=");
   1474     GNUNET_buffer_write_data_encoded (&buf,
   1475                                       (char *) claim_token,
   1476                                       sizeof (*claim_token));
   1477     num_qp++;
   1478   }
   1479 
   1480   if (NULL != session_id)
   1481   {
   1482     if (num_qp > 0)
   1483       GNUNET_buffer_write_str (&buf,
   1484                                "&session_id=");
   1485     else
   1486       GNUNET_buffer_write_str (&buf,
   1487                                "?session_id=");
   1488     GNUNET_buffer_write_str (&buf,
   1489                              session_id);
   1490     num_qp++;
   1491   }
   1492 
   1493   if (NULL != h_contract)
   1494   {
   1495     if (num_qp > 0)
   1496       GNUNET_buffer_write_str (&buf,
   1497                                "&h_contract=");
   1498     else
   1499       GNUNET_buffer_write_str (&buf,
   1500                                "?h_contract=");
   1501     GNUNET_buffer_write_data_encoded (&buf,
   1502                                       (char *) h_contract,
   1503                                       sizeof (*h_contract));
   1504   }
   1505 
   1506   return GNUNET_buffer_reap_str (&buf);
   1507 }
   1508 
   1509 
   1510 char *
   1511 TMH_make_taler_pay_uri (struct MHD_Connection *con,
   1512                         const char *order_id,
   1513                         const char *session_id,
   1514                         const char *instance_id,
   1515                         struct TALER_ClaimTokenP *claim_token)
   1516 {
   1517   struct GNUNET_Buffer buf;
   1518 
   1519   GNUNET_assert (NULL != instance_id);
   1520   GNUNET_assert (NULL != order_id);
   1521   if (GNUNET_OK !=
   1522       TMH_taler_uri_by_connection (con,
   1523                                    "pay",
   1524                                    instance_id,
   1525                                    &buf))
   1526   {
   1527     GNUNET_break (0);
   1528     return NULL;
   1529   }
   1530   GNUNET_buffer_write_path (&buf,
   1531                             order_id);
   1532   GNUNET_buffer_write_path (&buf,
   1533                             (NULL == session_id)
   1534                             ? ""
   1535                             : session_id);
   1536   if ( (NULL != claim_token) &&
   1537        (! GNUNET_is_zero (claim_token)))
   1538   {
   1539     /* Just 'c=' because this goes into QR
   1540        codes, so this is more compact. */
   1541     GNUNET_buffer_write_str (&buf,
   1542                              "?c=");
   1543     GNUNET_buffer_write_data_encoded (&buf,
   1544                                       (char *) claim_token,
   1545                                       sizeof (struct TALER_ClaimTokenP));
   1546   }
   1547 
   1548   return GNUNET_buffer_reap_str (&buf);
   1549 }