merchant

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

taler-merchant-httpd_private-post-templates.c (8915B)


      1 /*
      2   This file is part of TALER
      3   (C) 2022-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Affero General Public License as
      7   published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 
     20 /**
     21  * @file taler-merchant-httpd_private-post-templates.c
     22  * @brief implementing POST /templates request handling
     23  * @author Priscilla HUANG
     24  */
     25 #include "platform.h"
     26 #include "taler-merchant-httpd_private-post-templates.h"
     27 #include "taler-merchant-httpd_helper.h"
     28 #include <taler/taler_json_lib.h>
     29 
     30 
     31 /**
     32  * Check if the two templates are identical.
     33  *
     34  * @param t1 template to compare
     35  * @param t2 other template to compare
     36  * @return true if they are 'equal', false if not or of payto_uris is not an array
     37  */
     38 static bool
     39 templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
     40                  const struct TALER_MERCHANTDB_TemplateDetails *t2)
     41 {
     42   return ( (0 == strcmp (t1->template_description,
     43                          t2->template_description)) &&
     44            ( ( (NULL == t1->otp_id) &&
     45                (NULL == t2->otp_id) ) ||
     46              ( (NULL != t1->otp_id) &&
     47                (NULL != t2->otp_id) &&
     48                (0 == strcmp (t1->otp_id,
     49                              t2->otp_id))) ) &&
     50            ( ( (NULL == t1->editable_defaults) &&
     51                (NULL == t2->editable_defaults) ) ||
     52              ( (NULL != t1->editable_defaults) &&
     53                (NULL != t2->editable_defaults) &&
     54                (1 == json_equal (t1->editable_defaults,
     55                                  t2->editable_defaults))) ) &&
     56            (1 == json_equal (t1->template_contract,
     57                              t2->template_contract)) );
     58 }
     59 
     60 
     61 MHD_RESULT
     62 TMH_private_post_templates (const struct TMH_RequestHandler *rh,
     63                             struct MHD_Connection *connection,
     64                             struct TMH_HandlerContext *hc)
     65 {
     66   struct TMH_MerchantInstance *mi = hc->instance;
     67   struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
     68   const char *template_id;
     69   enum GNUNET_DB_QueryStatus qs;
     70   struct GNUNET_JSON_Specification spec[] = {
     71     GNUNET_JSON_spec_string ("template_id",
     72                              &template_id),
     73     GNUNET_JSON_spec_string ("template_description",
     74                              (const char **) &tp.template_description),
     75     GNUNET_JSON_spec_mark_optional (
     76       GNUNET_JSON_spec_string ("otp_id",
     77                                (const char **) &tp.otp_id),
     78       NULL),
     79     GNUNET_JSON_spec_json ("template_contract",
     80                            &tp.template_contract),
     81     GNUNET_JSON_spec_mark_optional (
     82       GNUNET_JSON_spec_json ("editable_defaults",
     83                              &tp.editable_defaults),
     84       NULL),
     85     GNUNET_JSON_spec_end ()
     86   };
     87   uint64_t otp_serial = 0;
     88 
     89   GNUNET_assert (NULL != mi);
     90   {
     91     enum GNUNET_GenericReturnValue res;
     92 
     93     res = TALER_MHD_parse_json_data (connection,
     94                                      hc->request_body,
     95                                      spec);
     96     if (GNUNET_OK != res)
     97     {
     98       GNUNET_break_op (0);
     99       return (GNUNET_NO == res)
    100              ? MHD_YES
    101              : MHD_NO;
    102     }
    103   }
    104   if (! TMH_template_contract_valid (tp.template_contract))
    105   {
    106     GNUNET_break_op (0);
    107     json_dumpf (tp.template_contract,
    108                 stderr,
    109                 JSON_INDENT (2));
    110     GNUNET_JSON_parse_free (spec);
    111     return TALER_MHD_reply_with_error (connection,
    112                                        MHD_HTTP_BAD_REQUEST,
    113                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    114                                        "template_contract");
    115   }
    116 
    117   if (NULL != tp.editable_defaults)
    118   {
    119     const char *key;
    120     json_t *val;
    121 
    122     json_object_foreach (tp.editable_defaults, key, val)
    123     {
    124       if (NULL !=
    125           json_object_get (tp.template_contract,
    126                            key))
    127       {
    128         char *msg;
    129         MHD_RESULT ret;
    130 
    131         GNUNET_break_op (0);
    132         GNUNET_asprintf (&msg,
    133                          "editable_defaults::%s conflicts with template_contract",
    134                          key);
    135         GNUNET_JSON_parse_free (spec);
    136         ret = TALER_MHD_reply_with_error (connection,
    137                                           MHD_HTTP_BAD_REQUEST,
    138                                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
    139                                           msg);
    140         GNUNET_free (msg);
    141         return ret;
    142       }
    143     }
    144   }
    145 
    146   if (NULL != tp.otp_id)
    147   {
    148     qs = TMH_db->select_otp_serial (TMH_db->cls,
    149                                     mi->settings.id,
    150                                     tp.otp_id,
    151                                     &otp_serial);
    152     switch (qs)
    153     {
    154     case GNUNET_DB_STATUS_HARD_ERROR:
    155     case GNUNET_DB_STATUS_SOFT_ERROR:
    156       GNUNET_break (0);
    157       GNUNET_JSON_parse_free (spec);
    158       return TALER_MHD_reply_with_error (connection,
    159                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    160                                          TALER_EC_GENERIC_DB_STORE_FAILED,
    161                                          "select_otp_serial");
    162     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    163       GNUNET_JSON_parse_free (spec);
    164       return TALER_MHD_reply_with_error (connection,
    165                                          MHD_HTTP_NOT_FOUND,
    166                                          TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
    167                                          NULL);
    168     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    169       break;
    170     }
    171   }
    172 
    173   qs = TMH_db->insert_template (TMH_db->cls,
    174                                 mi->settings.id,
    175                                 template_id,
    176                                 otp_serial,
    177                                 &tp);
    178   switch (qs)
    179   {
    180   case GNUNET_DB_STATUS_HARD_ERROR:
    181   case GNUNET_DB_STATUS_SOFT_ERROR:
    182     GNUNET_break (0);
    183     GNUNET_JSON_parse_free (spec);
    184     return TALER_MHD_reply_with_error (connection,
    185                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    186                                        TALER_EC_GENERIC_DB_STORE_FAILED,
    187                                        NULL);
    188   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    189     GNUNET_JSON_parse_free (spec);
    190     return TALER_MHD_reply_static (connection,
    191                                    MHD_HTTP_NO_CONTENT,
    192                                    NULL,
    193                                    NULL,
    194                                    0);
    195   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    196     break;
    197   }
    198 
    199   {
    200     /* Test if a template of this id is known */
    201     struct TALER_MERCHANTDB_TemplateDetails etp;
    202 
    203     qs = TMH_db->lookup_template (TMH_db->cls,
    204                                   mi->settings.id,
    205                                   template_id,
    206                                   &etp);
    207     switch (qs)
    208     {
    209     case GNUNET_DB_STATUS_HARD_ERROR:
    210     case GNUNET_DB_STATUS_SOFT_ERROR:
    211       /* Clean up and fail hard */
    212       GNUNET_break (0);
    213       GNUNET_JSON_parse_free (spec);
    214       return TALER_MHD_reply_with_error (connection,
    215                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    216                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    217                                          NULL);
    218     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    219       GNUNET_break (0);
    220       GNUNET_JSON_parse_free (spec);
    221       return TALER_MHD_reply_with_error (connection,
    222                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    223                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    224                                          "logic error");
    225     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    226       break;
    227     }
    228     /* idempotency check: is etp == tp? */
    229     {
    230       bool eq;
    231 
    232       eq = templates_equal (&tp,
    233                             &etp);
    234       TALER_MERCHANTDB_template_details_free (&etp);
    235       TMH_db->rollback (TMH_db->cls);
    236       GNUNET_JSON_parse_free (spec);
    237       return eq
    238         ? TALER_MHD_reply_static (connection,
    239                                   MHD_HTTP_NO_CONTENT,
    240                                   NULL,
    241                                   NULL,
    242                                   0)
    243         : TALER_MHD_reply_with_error (connection,
    244                                       MHD_HTTP_CONFLICT,
    245                                       TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
    246                                       template_id);
    247     }
    248   }
    249 }
    250 
    251 
    252 /* end of taler-merchant-httpd_private-post-templates.c */