merchant

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

taler-merchant-httpd_helper.c (36461B)


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