merchant

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

taler-merchant-httpd_helper.c (39508B)


      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 (GNUNET_OK !=
    357       GNUNET_JSON_parse (location,
    358                          spec,
    359                          &ename,
    360                          &eline))
    361   {
    362     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    363                 "Invalid location for field %s\n",
    364                 ename);
    365     return false;
    366   }
    367   if (NULL != lines)
    368   {
    369     size_t idx;
    370     json_t *line;
    371 
    372     json_array_foreach (lines, idx, line)
    373     {
    374       if (! json_is_string (line))
    375       {
    376         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    377                     "Invalid address line #%u in location\n",
    378                     (unsigned int) idx);
    379         return false;
    380       }
    381     }
    382   }
    383   return true;
    384 }
    385 
    386 
    387 bool
    388 TMH_products_array_valid (const json_t *products)
    389 {
    390   const json_t *product;
    391   size_t idx;
    392   bool valid = true;
    393 
    394   if (! json_is_array (products))
    395   {
    396     GNUNET_break_op (0);
    397     return false;
    398   }
    399   json_array_foreach ((json_t *) products, idx, product)
    400   {
    401     /* FIXME: use TALER_MERCHANT_parse_product() instead? */
    402     const char *product_id = NULL;
    403     const char *description;
    404     uint64_t quantity = 0;
    405     bool quantity_missing = true;
    406     const char *unit_quantity = NULL;
    407     bool unit_quantity_missing = true;
    408     const char *unit = NULL;
    409     struct TALER_Amount price = { .value = 0 };
    410     const char *image_data_url = NULL;
    411     const json_t *taxes = NULL;
    412     struct GNUNET_TIME_Timestamp delivery_date = { 0 };
    413     struct GNUNET_JSON_Specification spec[] = {
    414       GNUNET_JSON_spec_mark_optional (
    415         TALER_JSON_spec_slug ("product_id",
    416                               &product_id),
    417         NULL),
    418       TALER_JSON_spec_i18n_str ("description",
    419                                 &description),
    420       GNUNET_JSON_spec_mark_optional (
    421         GNUNET_JSON_spec_uint64 ("quantity",
    422                                  &quantity),
    423         &quantity_missing),
    424       GNUNET_JSON_spec_mark_optional (
    425         GNUNET_JSON_spec_string ("unit_quantity",
    426                                  &unit_quantity),
    427         &unit_quantity_missing),
    428       GNUNET_JSON_spec_mark_optional (
    429         TALER_JSON_spec_slug ("unit",
    430                               &unit),
    431         NULL),
    432       GNUNET_JSON_spec_mark_optional (
    433         TALER_JSON_spec_amount_any ("price",
    434                                     &price),
    435         NULL),
    436       GNUNET_JSON_spec_mark_optional (
    437         GNUNET_JSON_spec_string ("image",
    438                                  &image_data_url),
    439         NULL),
    440       GNUNET_JSON_spec_mark_optional (
    441         GNUNET_JSON_spec_array_const ("taxes",
    442                                       &taxes),
    443         NULL),
    444       GNUNET_JSON_spec_mark_optional (
    445         GNUNET_JSON_spec_timestamp ("delivery_date",
    446                                     &delivery_date),
    447         NULL),
    448       GNUNET_JSON_spec_end ()
    449     };
    450     const char *ename;
    451     unsigned int eline;
    452 
    453     if (GNUNET_OK !=
    454         GNUNET_JSON_parse (product,
    455                            spec,
    456                            &ename,
    457                            &eline))
    458     {
    459       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    460                   "Invalid product #%u for field %s\n",
    461                   (unsigned int) idx,
    462                   ename);
    463       return false;
    464     }
    465     if (quantity_missing)
    466     {
    467       if (unit_quantity_missing)
    468       {
    469         GNUNET_break_op (0);
    470         valid = false;
    471       }
    472     }
    473     if ( (NULL != image_data_url) &&
    474          (! TALER_MERCHANT_image_data_url_valid (image_data_url)) )
    475     {
    476       GNUNET_break_op (0);
    477       valid = false;
    478     }
    479     if ( (NULL != taxes) &&
    480          (! TALER_MERCHANT_taxes_array_valid (taxes)) )
    481     {
    482       GNUNET_break_op (0);
    483       valid = false;
    484     }
    485     GNUNET_JSON_parse_free (spec);
    486     if (! valid)
    487       break;
    488   }
    489 
    490   return valid;
    491 }
    492 
    493 
    494 enum GNUNET_GenericReturnValue
    495 TMH_validate_unit_price_array (const struct TALER_Amount *prices,
    496                                size_t prices_len)
    497 {
    498   /* Check for duplicate currencies */
    499   for (size_t i = 0; i < prices_len; i++)
    500   {
    501     for (size_t j = i + 1; j < prices_len; j++)
    502     {
    503       if (GNUNET_YES ==
    504           TALER_amount_cmp_currency (&prices[i],
    505                                      &prices[j]))
    506       {
    507         /* duplicate currency, not allowed! */
    508         GNUNET_break_op (0);
    509         return GNUNET_SYSERR;
    510       }
    511     }
    512   }
    513   return GNUNET_OK;
    514 }
    515 
    516 
    517 bool
    518 TMH_category_set_contains (const struct TMH_CategorySet *set,
    519                            uint64_t id)
    520 {
    521   for (size_t i = 0; i < set->len; i++)
    522     if (set->ids[i] == id)
    523       return true;
    524   return false;
    525 }
    526 
    527 
    528 void
    529 TMH_category_set_add (struct TMH_CategorySet *set,
    530                       uint64_t id)
    531 {
    532   if (TMH_category_set_contains (set,
    533                                  id))
    534     return;
    535   GNUNET_array_append (set->ids,
    536                        set->len,
    537                        id);
    538 }
    539 
    540 
    541 bool
    542 TMH_unit_set_contains (const struct TMH_UnitSet *set,
    543                        const char *unit)
    544 {
    545   for (unsigned int i = 0; i < set->len; i++)
    546     if (0 == strcmp (set->units[i],
    547                      unit))
    548       return true;
    549   return false;
    550 }
    551 
    552 
    553 void
    554 TMH_unit_set_add (struct TMH_UnitSet *set,
    555                   const char *unit)
    556 {
    557   if (TMH_unit_set_contains (set,
    558                              unit))
    559     return;
    560   GNUNET_array_append (set->units,
    561                        set->len,
    562                        GNUNET_strdup (unit));
    563 }
    564 
    565 
    566 void
    567 TMH_unit_set_clear (struct TMH_UnitSet *set)
    568 {
    569   for (unsigned int i = 0; i < set->len; i++)
    570     GNUNET_free (set->units[i]);
    571   GNUNET_free (set->units);
    572   set->units = NULL;
    573   set->len = 0;
    574 }
    575 
    576 
    577 struct TMH_WireMethod *
    578 TMH_setup_wire_account (
    579   struct TALER_FullPayto payto_uri,
    580   const char *credit_facade_url,
    581   const json_t *credit_facade_credentials)
    582 {
    583   struct TMH_WireMethod *wm;
    584   char *emsg;
    585 
    586   if (NULL != (emsg = TALER_payto_validate (payto_uri)))
    587   {
    588     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    589                 "Invalid URI `%s': %s\n",
    590                 payto_uri.full_payto,
    591                 emsg);
    592     GNUNET_free (emsg);
    593     return NULL;
    594   }
    595 
    596   wm = GNUNET_new (struct TMH_WireMethod);
    597   if (NULL != credit_facade_url)
    598     wm->credit_facade_url
    599       = GNUNET_strdup (credit_facade_url);
    600   if (NULL != credit_facade_credentials)
    601     wm->credit_facade_credentials
    602       = json_incref ((json_t*) credit_facade_credentials);
    603   GNUNET_CRYPTO_random_block (&wm->wire_salt,
    604                               sizeof (wm->wire_salt));
    605   wm->payto_uri.full_payto
    606     = GNUNET_strdup (payto_uri.full_payto);
    607   TALER_merchant_wire_signature_hash (payto_uri,
    608                                       &wm->wire_salt,
    609                                       &wm->h_wire);
    610   wm->wire_method
    611     = TALER_payto_get_method (payto_uri.full_payto);
    612   wm->active = true;
    613   return wm;
    614 }
    615 
    616 
    617 enum TALER_ErrorCode
    618 TMH_check_token (const char *token,
    619                  const char *instance_id,
    620                  enum TMH_AuthScope *as)
    621 {
    622   enum TMH_AuthScope scope;
    623   struct GNUNET_TIME_Timestamp expiration;
    624   enum GNUNET_DB_QueryStatus qs;
    625   struct TALER_MERCHANTDB_LoginTokenP btoken;
    626 
    627   if (NULL == token)
    628   {
    629     *as = TMH_AS_NONE;
    630     return TALER_EC_NONE;
    631   }
    632   if (0 != strncasecmp (token,
    633                         RFC_8959_PREFIX,
    634                         strlen (RFC_8959_PREFIX)))
    635   {
    636     *as = TMH_AS_NONE;
    637     return TALER_EC_NONE;
    638   }
    639   token += strlen (RFC_8959_PREFIX);
    640   if (GNUNET_OK !=
    641       GNUNET_STRINGS_string_to_data (token,
    642                                      strlen (token),
    643                                      &btoken,
    644                                      sizeof (btoken)))
    645   {
    646     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    647                 "Given authorization token `%s' is malformed\n",
    648                 token);
    649     GNUNET_break_op (0);
    650     return TALER_EC_GENERIC_TOKEN_MALFORMED;
    651   }
    652   qs = TALER_MERCHANTDB_get_login_token (TMH_db,
    653                                          instance_id,
    654                                          &btoken,
    655                                          &expiration,
    656                                          (uint32_t*) &scope);
    657   if (qs < 0)
    658   {
    659     GNUNET_break (0);
    660     return TALER_EC_GENERIC_DB_FETCH_FAILED;
    661   }
    662   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    663   {
    664     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    665                 "Authorization token `%s' unknown\n",
    666                 token);
    667     return TALER_EC_GENERIC_TOKEN_UNKNOWN;
    668   }
    669   if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
    670   {
    671     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    672                 "Authorization token `%s' expired\n",
    673                 token);
    674     return TALER_EC_GENERIC_TOKEN_EXPIRED;
    675   }
    676   *as = scope;
    677   return TALER_EC_NONE;
    678 }
    679 
    680 
    681 enum GNUNET_GenericReturnValue
    682 TMH_check_auth_config (struct MHD_Connection *connection,
    683                        const json_t *jauth,
    684                        const char **auth_password)
    685 {
    686   bool auth_wellformed = false;
    687   const char *auth_method = json_string_value (json_object_get (jauth,
    688                                                                 "method"));
    689 
    690   *auth_password = NULL;
    691   if (NULL == auth_method)
    692   {
    693     GNUNET_break_op (0);
    694   }
    695   else if ((GNUNET_YES != TMH_strict_v19) &&
    696            (0 == strcmp (auth_method,
    697                          "external")))
    698   {
    699     auth_wellformed = true;
    700   }
    701   else if (GNUNET_YES == TMH_auth_disabled)
    702   {
    703     auth_wellformed = true;
    704   }
    705   else if (0 == strcmp (auth_method,
    706                         "token"))
    707   {
    708     json_t *pw_value;
    709 
    710     pw_value = json_object_get (jauth,
    711                                 "password");
    712     if (NULL == pw_value)
    713     {
    714       pw_value = json_object_get (jauth,
    715                                   "token");
    716     }
    717     if (NULL == pw_value)
    718     {
    719       auth_wellformed = false;
    720       GNUNET_break_op (0);
    721     }
    722     else
    723     {
    724       *auth_password = json_string_value (pw_value);
    725       if (NULL != *auth_password)
    726       {
    727         if (0 == strncasecmp (RFC_8959_PREFIX,
    728                               *auth_password,
    729                               strlen (RFC_8959_PREFIX)))
    730         {
    731           *auth_password = *auth_password + strlen (RFC_8959_PREFIX);
    732         }
    733         auth_wellformed = true;
    734       }
    735     }
    736   }
    737 
    738   if (! auth_wellformed)
    739   {
    740     GNUNET_break_op (0);
    741     return (MHD_YES ==
    742             TALER_MHD_reply_with_error (connection,
    743                                         MHD_HTTP_BAD_REQUEST,
    744                                         TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH,
    745                                         "bad authentication config"))
    746       ? GNUNET_NO
    747       : GNUNET_SYSERR;
    748   }
    749   return GNUNET_OK;
    750 }
    751 
    752 
    753 void
    754 TMH_uuid_from_string (const char *uuids,
    755                       struct GNUNET_Uuid *uuid)
    756 {
    757   struct GNUNET_HashCode hc;
    758 
    759   GNUNET_static_assert (sizeof (hc) > sizeof (*uuid));
    760   GNUNET_CRYPTO_hash (uuids,
    761                       strlen (uuids),
    762                       &hc);
    763   GNUNET_memcpy (uuid,
    764                  &hc,
    765                  sizeof (*uuid));
    766 }
    767 
    768 
    769 /**
    770  * Closure for #trigger_webhook_cb.
    771  *
    772  * @param instance which is the instance we work with
    773  * @param root JSON data to fill into the template
    774  * @param rv, query of the TALER_TEMPLATEING_fill
    775  */
    776 struct Trigger
    777 {
    778   const char *instance;
    779 
    780   const json_t *root;
    781 
    782   enum GNUNET_DB_QueryStatus rv;
    783 
    784 };
    785 
    786 /**
    787  * Typically called by `TMH_trigger_webhook`.
    788  *
    789  * @param[in,out] cls a `struct Trigger` with information about the webhook
    790  * @param webhook_serial reference to the configured webhook template.
    791  * @param event_type is the event/action of the webhook
    792  * @param url to make request to
    793  * @param http_method use for the webhook
    794  * @param header_template of the webhook
    795  * @param body_template of the webhook
    796  */
    797 static void
    798 trigger_webhook_cb (void *cls,
    799                     uint64_t webhook_serial,
    800                     const char *event_type,
    801                     const char *url,
    802                     const char *http_method,
    803                     const char *header_template,
    804                     const char *body_template)
    805 {
    806   struct Trigger *t = cls;
    807   void *header = NULL;
    808   void *body = NULL;
    809   size_t header_size;
    810   size_t body_size;
    811 
    812   if (NULL != header_template)
    813   {
    814     int ret;
    815 
    816     ret = TALER_TEMPLATING_fill (header_template,
    817                                  t->root,
    818                                  &header,
    819                                  &header_size);
    820     if (0 != ret)
    821     {
    822       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    823                   "Failed to expand webhook header template for webhook %llu (%d)\n",
    824                   (unsigned long long) webhook_serial,
    825                   ret);
    826       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
    827       return;
    828     }
    829     /* Note: header is actually header_size+1 bytes long here, see mustach.c::memfile_close() */
    830     GNUNET_assert ('\0' == ((const char *) header)[header_size]);
    831   }
    832   if (NULL != body_template)
    833   {
    834     int ret;
    835 
    836     ret = TALER_TEMPLATING_fill (body_template,
    837                                  t->root,
    838                                  &body,
    839                                  &body_size);
    840     if (0 != ret)
    841     {
    842       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    843                   "Failed to expand webhook body template for webhook %llu (%d)\n",
    844                   (unsigned long long) webhook_serial,
    845                   ret);
    846       t->rv = GNUNET_DB_STATUS_HARD_ERROR;
    847       return;
    848     }
    849     /* Note: body is actually body_size+1 bytes long here, see mustach.c::memfile_close() */
    850     GNUNET_assert ('\0' == ((const char *) body)[body_size]);
    851   }
    852   t->rv = TALER_MERCHANTDB_insert_pending_webhook (TMH_db,
    853                                                    t->instance,
    854                                                    webhook_serial,
    855                                                    url,
    856                                                    http_method,
    857                                                    header,
    858                                                    body);
    859   if (t->rv > 0)
    860   {
    861     struct GNUNET_DB_EventHeaderP es = {
    862       .size = htons (sizeof(es)),
    863       .type = htons (TALER_DBEVENT_MERCHANT_WEBHOOK_PENDING)
    864     };
    865     const void *extra = NULL;
    866     size_t extra_size = 0;
    867     TALER_MERCHANTDB_event_notify (TMH_db,
    868                                    &es,
    869                                    &extra,
    870                                    extra_size);
    871   }
    872   /* allocated by mustach, hence use free(), not GNUNET_free() */
    873   free (header);
    874   free (body);
    875 }
    876 
    877 
    878 /**
    879  * TMH_trigger_webhook is a function that need to be use when someone
    880  * pay. Merchant need to have a notification.
    881  *
    882  * @param instance that we need to send the webhook as a notification
    883  * @param event of the webhook
    884  * @param args argument of the function
    885  */
    886 enum GNUNET_DB_QueryStatus
    887 TMH_trigger_webhook (const char *instance,
    888                      const char *event,
    889                      const json_t *args)
    890 {
    891   struct Trigger t = {
    892     .instance = instance,
    893     .root = args
    894   };
    895   enum GNUNET_DB_QueryStatus qs;
    896 
    897   qs = TALER_MERCHANTDB_iterate_webhooks_by_event (TMH_db,
    898                                                    instance,
    899                                                    event,
    900                                                    &trigger_webhook_cb,
    901                                                    &t);
    902   if (qs < 0)
    903     return qs;
    904   return t.rv;
    905 }
    906 
    907 
    908 enum GNUNET_GenericReturnValue
    909 TMH_base_url_by_connection (struct MHD_Connection *connection,
    910                             const char *instance,
    911                             struct GNUNET_Buffer *buf)
    912 {
    913   const char *host;
    914   const char *forwarded_host;
    915   const char *forwarded_port;
    916   const char *uri_path;
    917 
    918   memset (buf,
    919           0,
    920           sizeof (*buf));
    921   if (NULL != TMH_base_url)
    922   {
    923     GNUNET_buffer_write_str (buf,
    924                              TMH_base_url);
    925   }
    926   else
    927   {
    928     if (GNUNET_YES ==
    929         TALER_mhd_is_https (connection))
    930       GNUNET_buffer_write_str (buf,
    931                                "https://");
    932     else
    933       GNUNET_buffer_write_str (buf,
    934                                "http://");
    935     host = MHD_lookup_connection_value (connection,
    936                                         MHD_HEADER_KIND,
    937                                         MHD_HTTP_HEADER_HOST);
    938     forwarded_host = MHD_lookup_connection_value (connection,
    939                                                   MHD_HEADER_KIND,
    940                                                   "X-Forwarded-Host");
    941     if (NULL != forwarded_host)
    942     {
    943       GNUNET_buffer_write_str (buf,
    944                                forwarded_host);
    945     }
    946     else
    947     {
    948       if (NULL == host)
    949       {
    950         GNUNET_buffer_clear (buf);
    951         GNUNET_break (0);
    952         return GNUNET_SYSERR;
    953       }
    954       GNUNET_buffer_write_str (buf,
    955                                host);
    956     }
    957     forwarded_port = MHD_lookup_connection_value (connection,
    958                                                   MHD_HEADER_KIND,
    959                                                   "X-Forwarded-Port");
    960     if (NULL != forwarded_port)
    961     {
    962       GNUNET_buffer_write_str (buf,
    963                                ":");
    964       GNUNET_buffer_write_str (buf,
    965                                forwarded_port);
    966     }
    967     uri_path = MHD_lookup_connection_value (connection,
    968                                             MHD_HEADER_KIND,
    969                                             "X-Forwarded-Prefix");
    970     if (NULL != uri_path)
    971       GNUNET_buffer_write_path (buf,
    972                                 uri_path);
    973   }
    974   if (0 != strcmp (instance,
    975                    "admin"))
    976   {
    977     GNUNET_buffer_write_path (buf,
    978                               "/instances/");
    979     GNUNET_buffer_write_str (buf,
    980                              instance);
    981   }
    982   return GNUNET_OK;
    983 }
    984 
    985 
    986 enum GNUNET_GenericReturnValue
    987 TMH_taler_uri_by_connection (struct MHD_Connection *connection,
    988                              const char *method,
    989                              const char *instance,
    990                              struct GNUNET_Buffer *buf)
    991 {
    992   const char *host;
    993   const char *forwarded_host;
    994   const char *forwarded_port;
    995   const char *uri_path;
    996 
    997   memset (buf,
    998           0,
    999           sizeof (*buf));
   1000   host = MHD_lookup_connection_value (connection,
   1001                                       MHD_HEADER_KIND,
   1002                                       "Host");
   1003   forwarded_host = MHD_lookup_connection_value (connection,
   1004                                                 MHD_HEADER_KIND,
   1005                                                 "X-Forwarded-Host");
   1006   forwarded_port = MHD_lookup_connection_value (connection,
   1007                                                 MHD_HEADER_KIND,
   1008                                                 "X-Forwarded-Port");
   1009   uri_path = MHD_lookup_connection_value (connection,
   1010                                           MHD_HEADER_KIND,
   1011                                           "X-Forwarded-Prefix");
   1012   if (NULL != forwarded_host)
   1013     host = forwarded_host;
   1014   if (NULL == host)
   1015   {
   1016     GNUNET_break (0);
   1017     return GNUNET_SYSERR;
   1018   }
   1019   GNUNET_buffer_write_str (buf,
   1020                            "taler");
   1021   if (GNUNET_NO == TALER_mhd_is_https (connection))
   1022     GNUNET_buffer_write_str (buf,
   1023                              "+http");
   1024   GNUNET_buffer_write_str (buf,
   1025                            "://");
   1026   GNUNET_buffer_write_str (buf,
   1027                            method);
   1028   GNUNET_buffer_write_str (buf,
   1029                            "/");
   1030   GNUNET_buffer_write_str (buf,
   1031                            host);
   1032   if (NULL != forwarded_port)
   1033   {
   1034     GNUNET_buffer_write_str (buf,
   1035                              ":");
   1036     GNUNET_buffer_write_str (buf,
   1037                              forwarded_port);
   1038   }
   1039   if (NULL != uri_path)
   1040     GNUNET_buffer_write_path (buf,
   1041                               uri_path);
   1042   if (0 != strcmp ("admin",
   1043                    instance))
   1044   {
   1045     GNUNET_buffer_write_path (buf,
   1046                               "instances");
   1047     GNUNET_buffer_write_path (buf,
   1048                               instance);
   1049   }
   1050   return GNUNET_OK;
   1051 }
   1052 
   1053 
   1054 /**
   1055  * Closure for #add_matching_account().
   1056  */
   1057 struct ExchangeMatchContext
   1058 {
   1059   /**
   1060    * Wire method to match, NULL for all.
   1061    */
   1062   const char *wire_method;
   1063 
   1064   /**
   1065    * Array of accounts to return.
   1066    */
   1067   json_t *accounts;
   1068 };
   1069 
   1070 
   1071 /**
   1072  * If the given account is feasible, add it to the array
   1073  * of accounts we return.
   1074  *
   1075  * @param cls a `struct PostReserveContext`
   1076  * @param payto_uri URI of the account
   1077  * @param conversion_url URL of a conversion service
   1078  * @param debit_restrictions restrictions for debits from account
   1079  * @param credit_restrictions restrictions for credits to account
   1080  * @param master_sig signature affirming the account
   1081  */
   1082 static void
   1083 add_matching_account (
   1084   void *cls,
   1085   struct TALER_FullPayto payto_uri,
   1086   const char *conversion_url,
   1087   const json_t *debit_restrictions,
   1088   const json_t *credit_restrictions,
   1089   const struct TALER_MasterSignatureP *master_sig)
   1090 {
   1091   struct ExchangeMatchContext *rc = cls;
   1092   char *method;
   1093 
   1094   method = TALER_payto_get_method (payto_uri.full_payto);
   1095   if ( (NULL == rc->wire_method) ||
   1096        (0 == strcmp (method,
   1097                      rc->wire_method)) )
   1098   {
   1099     json_t *acc;
   1100 
   1101     acc = GNUNET_JSON_PACK (
   1102       TALER_JSON_pack_full_payto ("payto_uri",
   1103                                   payto_uri),
   1104       GNUNET_JSON_pack_data_auto ("master_sig",
   1105                                   master_sig),
   1106       GNUNET_JSON_pack_allow_null (
   1107         GNUNET_JSON_pack_string ("conversion_url",
   1108                                  conversion_url)),
   1109       GNUNET_JSON_pack_array_incref ("credit_restrictions",
   1110                                      (json_t *) credit_restrictions),
   1111       GNUNET_JSON_pack_array_incref ("debit_restrictions",
   1112                                      (json_t *) debit_restrictions)
   1113       );
   1114     GNUNET_assert (0 ==
   1115                    json_array_append_new (rc->accounts,
   1116                                           acc));
   1117   }
   1118   GNUNET_free (method);
   1119 }
   1120 
   1121 
   1122 /**
   1123  * Return JSON array with all of the exchange accounts
   1124  * that support the given @a wire_method.
   1125  *
   1126  * @param master_pub master public key to match exchange by
   1127  * @param wire_method NULL for any
   1128  * @return JSON array with information about all matching accounts
   1129  */
   1130 json_t *
   1131 TMH_exchange_accounts_by_method (
   1132   const struct TALER_MasterPublicKeyP *master_pub,
   1133   const char *wire_method)
   1134 {
   1135   struct ExchangeMatchContext emc = {
   1136     .wire_method = wire_method,
   1137     .accounts = json_array ()
   1138   };
   1139   enum GNUNET_DB_QueryStatus qs;
   1140 
   1141   GNUNET_assert (NULL != emc.accounts);
   1142   qs = TALER_MERCHANTDB_iterate_exchange_accounts (TMH_db,
   1143                                                    master_pub,
   1144                                                    &add_matching_account,
   1145                                                    &emc);
   1146   if (qs < 0)
   1147   {
   1148     json_decref (emc.accounts);
   1149     return NULL;
   1150   }
   1151   return emc.accounts;
   1152 }
   1153 
   1154 
   1155 char *
   1156 TMH_make_order_status_url (struct MHD_Connection *con,
   1157                            const char *order_id,
   1158                            const char *session_id,
   1159                            const char *instance_id,
   1160                            struct TALER_ClaimTokenP *claim_token,
   1161                            struct TALER_PrivateContractHashP *h_contract)
   1162 {
   1163   struct GNUNET_Buffer buf;
   1164   /* Number of query parameters written so far */
   1165   unsigned int num_qp = 0;
   1166 
   1167   GNUNET_assert (NULL != instance_id);
   1168   GNUNET_assert (NULL != order_id);
   1169   if (GNUNET_OK !=
   1170       TMH_base_url_by_connection (con,
   1171                                   instance_id,
   1172                                   &buf))
   1173   {
   1174     GNUNET_break (0);
   1175     return NULL;
   1176   }
   1177   GNUNET_buffer_write_path (&buf,
   1178                             "/orders");
   1179   GNUNET_buffer_write_path (&buf,
   1180                             order_id);
   1181   if ( (NULL != claim_token) &&
   1182        (! GNUNET_is_zero (claim_token)) )
   1183   {
   1184     /* 'token=' for human readability */
   1185     GNUNET_buffer_write_str (&buf,
   1186                              "?token=");
   1187     GNUNET_buffer_write_data_encoded (&buf,
   1188                                       (char *) claim_token,
   1189                                       sizeof (*claim_token));
   1190     num_qp++;
   1191   }
   1192 
   1193   if (NULL != session_id)
   1194   {
   1195     if (num_qp > 0)
   1196       GNUNET_buffer_write_str (&buf,
   1197                                "&session_id=");
   1198     else
   1199       GNUNET_buffer_write_str (&buf,
   1200                                "?session_id=");
   1201     GNUNET_buffer_write_str (&buf,
   1202                              session_id);
   1203     num_qp++;
   1204   }
   1205 
   1206   if (NULL != h_contract)
   1207   {
   1208     if (num_qp > 0)
   1209       GNUNET_buffer_write_str (&buf,
   1210                                "&h_contract=");
   1211     else
   1212       GNUNET_buffer_write_str (&buf,
   1213                                "?h_contract=");
   1214     GNUNET_buffer_write_data_encoded (&buf,
   1215                                       (char *) h_contract,
   1216                                       sizeof (*h_contract));
   1217   }
   1218 
   1219   return GNUNET_buffer_reap_str (&buf);
   1220 }
   1221 
   1222 
   1223 char *
   1224 TMH_make_taler_pay_uri (struct MHD_Connection *con,
   1225                         const char *order_id,
   1226                         const char *session_id,
   1227                         const char *instance_id,
   1228                         struct TALER_ClaimTokenP *claim_token)
   1229 {
   1230   struct GNUNET_Buffer buf;
   1231 
   1232   GNUNET_assert (NULL != instance_id);
   1233   GNUNET_assert (NULL != order_id);
   1234   if (GNUNET_OK !=
   1235       TMH_taler_uri_by_connection (con,
   1236                                    "pay",
   1237                                    instance_id,
   1238                                    &buf))
   1239   {
   1240     GNUNET_break (0);
   1241     return NULL;
   1242   }
   1243   GNUNET_buffer_write_path (&buf,
   1244                             order_id);
   1245   GNUNET_buffer_write_path (&buf,
   1246                             (NULL == session_id)
   1247                             ? ""
   1248                             : session_id);
   1249   if ( (NULL != claim_token) &&
   1250        (! GNUNET_is_zero (claim_token)))
   1251   {
   1252     /* Just 'c=' because this goes into QR
   1253        codes, so this is more compact. */
   1254     GNUNET_buffer_write_str (&buf,
   1255                              "?c=");
   1256     GNUNET_buffer_write_data_encoded (&buf,
   1257                                       (char *) claim_token,
   1258                                       sizeof (struct TALER_ClaimTokenP));
   1259   }
   1260 
   1261   return GNUNET_buffer_reap_str (&buf);
   1262 }
   1263 
   1264 
   1265 enum GNUNET_GenericReturnValue
   1266 TMH_compute_order_total (const json_t *contract_terms,
   1267                          int16_t choice_index,
   1268                          struct TALER_Amount *total)
   1269 {
   1270   enum TALER_MERCHANT_ContractVersion version
   1271     = TALER_MERCHANT_CONTRACT_VERSION_0;
   1272   const json_t *amount_bearer = contract_terms;
   1273   struct TALER_Amount amount;
   1274   const json_t *amount_external;
   1275   struct GNUNET_JSON_Specification spec[] = {
   1276     GNUNET_JSON_spec_mark_optional (
   1277       TALER_MERCHANT_spec_contract_version ("version",
   1278                                             &version),
   1279       NULL),
   1280     GNUNET_JSON_spec_end ()
   1281   };
   1282 
   1283   if (GNUNET_OK !=
   1284       GNUNET_JSON_parse (contract_terms,
   1285                          spec,
   1286                          NULL,
   1287                          NULL))
   1288   {
   1289     GNUNET_break (0);
   1290     return GNUNET_SYSERR;
   1291   }
   1292   if (TALER_MERCHANT_CONTRACT_VERSION_1 == version)
   1293   {
   1294     if (0 > choice_index)
   1295     {
   1296       /* Without a selected choice the total is undefined; the caller
   1297          must only ask for orders where a choice was picked. */
   1298       GNUNET_break (0);
   1299       return GNUNET_SYSERR;
   1300     }
   1301     amount_bearer = json_array_get (
   1302       json_object_get (contract_terms,
   1303                        "choices"),
   1304       (size_t) choice_index);
   1305     if (NULL == amount_bearer)
   1306     {
   1307       GNUNET_break (0);
   1308       return GNUNET_SYSERR;
   1309     }
   1310   }
   1311   {
   1312     struct GNUNET_JSON_Specification aspec[] = {
   1313       TALER_JSON_spec_amount_any ("amount",
   1314                                   &amount),
   1315       GNUNET_JSON_spec_end ()
   1316     };
   1317 
   1318     if (GNUNET_OK !=
   1319         GNUNET_JSON_parse (amount_bearer,
   1320                            aspec,
   1321                            NULL,
   1322                            NULL))
   1323     {
   1324       GNUNET_break (0);
   1325       return GNUNET_SYSERR;
   1326     }
   1327   }
   1328   *total = amount;
   1329   amount_external = json_object_get (contract_terms,
   1330                                      "amount_external");
   1331   {
   1332     size_t idx;
   1333     json_t *entry;
   1334 
   1335     json_array_foreach ((json_t *) amount_external, idx, entry)
   1336     {
   1337       struct TALER_Amount ext_amount;
   1338       struct GNUNET_JSON_Specification espec[] = {
   1339         TALER_JSON_spec_amount_any ("amount",
   1340                                     &ext_amount),
   1341         GNUNET_JSON_spec_end ()
   1342       };
   1343 
   1344       if (GNUNET_OK !=
   1345           GNUNET_JSON_parse (entry,
   1346                              espec,
   1347                              NULL,
   1348                              NULL))
   1349       {
   1350         GNUNET_break (0);
   1351         return GNUNET_SYSERR;
   1352       }
   1353       if (0 >
   1354           TALER_amount_add (total,
   1355                             total,
   1356                             &ext_amount))
   1357       {
   1358         GNUNET_break (0);
   1359         return GNUNET_SYSERR;
   1360       }
   1361     }
   1362   }
   1363   return GNUNET_OK;
   1364 }