merchant

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

taler-merchant-httpd_helper.c (39664B)


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