merchant

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

order_choice_parse.c (13626B)


      1 /*
      2   This file is part of TALER
      3   (C) 2024, 2025 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/util/order_choice_parse.c
     18  * @brief shared logic for order choice parsing
     19  * @author Iván Ávalos
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include <gnunet/gnunet_common.h>
     24 #include <gnunet/gnunet_json_lib.h>
     25 #include <jansson.h>
     26 #include <stdbool.h>
     27 #include <stdint.h>
     28 #include <taler/taler_json_lib.h>
     29 #include <taler/taler_util.h>
     30 #include "taler/taler_merchant_util.h"
     31 
     32 
     33 /**
     34  * Parse JSON order choice input.
     35  *
     36  * @param[in] root JSON object containing choice input
     37  * @param[out] input parsed choice input, NULL if @a input is malformed
     38  * @param index index of choice input in inputs array
     39  * @return #GNUNET_SYSERR if @a input is malformed; #GNUNET_OK otherwise
     40  */
     41 static enum GNUNET_GenericReturnValue
     42 parse_order_choice_input (
     43   json_t *root,
     44   struct TALER_MERCHANT_OrderInput *input,
     45   size_t index)
     46 {
     47   const char *ename;
     48   unsigned int eline;
     49   struct GNUNET_JSON_Specification ispec[] = {
     50     TALER_MERCHANT_json_spec_cit ("type",
     51                                   &input->type),
     52     GNUNET_JSON_spec_end ()
     53   };
     54 
     55   if (GNUNET_OK !=
     56       GNUNET_JSON_parse (root,
     57                          ispec,
     58                          &ename,
     59                          &eline))
     60   {
     61     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     62                 "Failed to parse %s at %u: %s\n",
     63                 ispec[eline].field,
     64                 eline,
     65                 ename);
     66     GNUNET_break_op (0);
     67     return GNUNET_SYSERR;
     68   }
     69 
     70   switch (input->type)
     71   {
     72   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     73     GNUNET_break (0);
     74     break;
     75   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     76     {
     77       struct GNUNET_JSON_Specification spec[] = {
     78         TALER_JSON_spec_slug ("token_family_slug",
     79                               &input->details.token.token_family_slug),
     80         GNUNET_JSON_spec_mark_optional (
     81           GNUNET_JSON_spec_uint ("count",
     82                                  &input->details.token.count),
     83           NULL),
     84         GNUNET_JSON_spec_end ()
     85       };
     86 
     87       if (GNUNET_OK !=
     88           GNUNET_JSON_parse (root,
     89                              spec,
     90                              &ename,
     91                              &eline))
     92       {
     93         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     94                     "Failed to parse %s at %u: %s\n",
     95                     spec[eline].field,
     96                     eline,
     97                     ename);
     98         GNUNET_break_op (0);
     99         return GNUNET_SYSERR;
    100       }
    101 
    102       return GNUNET_OK;
    103     }
    104   }
    105 
    106   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    107               "Field 'type' invalid in input #%u\n",
    108               (unsigned int) index);
    109   GNUNET_break_op (0);
    110   return GNUNET_SYSERR;
    111 }
    112 
    113 
    114 /**
    115  * Parse JSON order choice output.
    116  *
    117  * @param[in] root JSON object containing choice output
    118  * @param[out] output parsed choice output, NULL if @a output is malformed
    119  * @param index index of choice output in outputs array
    120  * @return #GNUNET_SYSERR if @a output is malformed; #GNUNET_OK otherwise
    121  */
    122 static enum GNUNET_GenericReturnValue
    123 parse_order_choice_output (
    124   json_t *root,
    125   struct TALER_MERCHANT_OrderOutput *output,
    126   size_t index)
    127 {
    128   const char *ename;
    129   unsigned int eline;
    130   struct GNUNET_JSON_Specification ispec[] = {
    131     TALER_MERCHANT_json_spec_cot ("type",
    132                                   &output->type),
    133     GNUNET_JSON_spec_end ()
    134   };
    135 
    136   if (GNUNET_OK !=
    137       GNUNET_JSON_parse (root,
    138                          ispec,
    139                          &ename,
    140                          &eline))
    141   {
    142     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    143                 "Failed to parse %s at %u: %s\n",
    144                 ispec[eline].field,
    145                 eline,
    146                 ename);
    147     GNUNET_break_op (0);
    148     return GNUNET_SYSERR;
    149   }
    150 
    151   switch (output->type)
    152   {
    153   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
    154     GNUNET_break (0);
    155     break;
    156   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
    157     {
    158       struct GNUNET_JSON_Specification spec[] = {
    159         TALER_JSON_spec_slug ("token_family_slug",
    160                               &output->details.token.token_family_slug),
    161         GNUNET_JSON_spec_mark_optional (
    162           GNUNET_JSON_spec_uint ("count",
    163                                  &output->details.token.count),
    164           NULL),
    165         GNUNET_JSON_spec_mark_optional (
    166           GNUNET_JSON_spec_timestamp ("valid_at",
    167                                       &output->details.token.valid_at),
    168           NULL),
    169         GNUNET_JSON_spec_end ()
    170       };
    171 
    172       if (GNUNET_OK !=
    173           GNUNET_JSON_parse (root,
    174                              spec,
    175                              &ename,
    176                              &eline))
    177       {
    178         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    179                     "Failed to parse %s at %u: %s\n",
    180                     spec[eline].field,
    181                     eline,
    182                     ename);
    183         GNUNET_break_op (0);
    184         return GNUNET_SYSERR;
    185       }
    186 
    187       return GNUNET_OK;
    188     }
    189   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
    190     {
    191       struct GNUNET_JSON_Specification spec[] = {
    192         GNUNET_JSON_spec_mark_optional (
    193           TALER_JSON_spec_amount_any ("amount",
    194                                       &output->details.donation_receipt.amount),
    195           &output->details.donation_receipt.no_amount),
    196         GNUNET_JSON_spec_end ()
    197       };
    198 
    199       if (GNUNET_OK !=
    200           GNUNET_JSON_parse (root,
    201                              spec,
    202                              &ename,
    203                              &eline))
    204       {
    205         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    206                     "Failed to parse %s at %u: %s\n",
    207                     spec[eline].field,
    208                     eline,
    209                     ename);
    210         GNUNET_break_op (0);
    211         return GNUNET_SYSERR;
    212       }
    213       return GNUNET_OK;
    214     }
    215   }
    216 
    217   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    218               "Field 'type' invalid in output #%u\n",
    219               (unsigned int) index);
    220   GNUNET_break_op (0);
    221   return GNUNET_SYSERR;
    222 }
    223 
    224 
    225 /**
    226  * Parse given JSON object to choices array.
    227  *
    228  * @param cls closure, pointer to array length
    229  * @param root the json array representing the choices
    230  * @param[out] ospec where to write the data
    231  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    232  */
    233 static enum GNUNET_GenericReturnValue
    234 parse_order_choices (
    235   void *cls,
    236   json_t *root,
    237   struct GNUNET_JSON_Specification *ospec)
    238 {
    239   struct TALER_MERCHANT_OrderChoice **choices = ospec->ptr;
    240   unsigned int *choices_len = cls;
    241 
    242   if (! json_is_array (root))
    243   {
    244     GNUNET_break_op (0);
    245     return GNUNET_SYSERR;
    246   }
    247   if (0 == json_array_size (root))
    248   {
    249     /* empty list of choices is not allowed */
    250     GNUNET_break_op (0);
    251     return GNUNET_SYSERR;
    252   }
    253   *choices = NULL;
    254   *choices_len = 0;
    255   GNUNET_array_grow (*choices,
    256                      *choices_len,
    257                      json_array_size (root));
    258 
    259   for (unsigned int i = 0; i < *choices_len; i++)
    260   {
    261     struct TALER_MERCHANT_OrderChoice *choice = &(*choices)[i];
    262     const json_t *jinputs = NULL;
    263     const json_t *joutputs = NULL;
    264     struct GNUNET_JSON_Specification spec[] = {
    265       TALER_JSON_spec_amount_any ("amount",
    266                                   &choice->amount),
    267       GNUNET_JSON_spec_mark_optional (
    268         TALER_JSON_spec_amount_any ("tip",
    269                                     &choice->tip),
    270         &choice->no_tip),
    271       GNUNET_JSON_spec_mark_optional (
    272         GNUNET_JSON_spec_string_copy ("description",
    273                                       &choice->description),
    274         NULL),
    275       GNUNET_JSON_spec_mark_optional (
    276         GNUNET_JSON_spec_object_copy ("description_i18n",
    277                                       &choice->description_i18n),
    278         NULL),
    279       GNUNET_JSON_spec_mark_optional (
    280         TALER_JSON_spec_amount_any ("max_fee",
    281                                     &choice->max_fee),
    282         &choice->no_max_fee),
    283       GNUNET_JSON_spec_mark_optional (
    284         GNUNET_JSON_spec_bool ("editable_amount",
    285                                &choice->editable_amount),
    286         NULL),
    287       GNUNET_JSON_spec_mark_optional (
    288         GNUNET_JSON_spec_array_const ("inputs",
    289                                       &jinputs),
    290         NULL),
    291       GNUNET_JSON_spec_mark_optional (
    292         GNUNET_JSON_spec_array_const ("outputs",
    293                                       &joutputs),
    294         NULL),
    295       GNUNET_JSON_spec_end ()
    296     };
    297     const char *ename;
    298     unsigned int eline;
    299 
    300     if (GNUNET_OK !=
    301         GNUNET_JSON_parse (json_array_get (root, i),
    302                            spec,
    303                            &ename,
    304                            &eline))
    305     {
    306       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    307                   "Failed to parse %s at %u: %s\n",
    308                   spec[eline].field,
    309                   eline,
    310                   ename);
    311       GNUNET_break_op (0);
    312       return GNUNET_SYSERR;
    313     }
    314     if ( (! choice->no_max_fee) &&
    315          (GNUNET_OK !=
    316           TALER_amount_cmp_currency (&choice->amount,
    317                                      &choice->max_fee)) )
    318     {
    319       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    320                   "Fee currency does not match amount currency in choice #%u\n",
    321                   i);
    322       GNUNET_break_op (0);
    323       return GNUNET_SYSERR;
    324     }
    325     if ( (! choice->no_tip) &&
    326          (GNUNET_OK !=
    327           TALER_amount_cmp_currency (&choice->amount,
    328                                      &choice->tip)) )
    329     {
    330       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    331                   "Tip currency does not match amount currency in choice #%u\n",
    332                   i);
    333       GNUNET_break_op (0);
    334       return GNUNET_SYSERR;
    335     }
    336 
    337     if (NULL != jinputs)
    338     {
    339       const json_t *jinput;
    340       size_t idx;
    341 
    342       json_array_foreach ((json_t *) jinputs, idx, jinput)
    343       {
    344         struct TALER_MERCHANT_OrderInput input = {
    345           .details.token.count = 1
    346         };
    347 
    348         if (GNUNET_OK !=
    349             parse_order_choice_input ((json_t *) jinput,
    350                                       &input,
    351                                       idx))
    352         {
    353           GNUNET_break (0);
    354           return GNUNET_SYSERR;
    355         }
    356         switch (input.type)
    357         {
    358         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
    359           GNUNET_break_op (0);
    360           return GNUNET_SYSERR;
    361         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
    362           /* Ignore inputs tokens with 'count' field set to 0 */
    363           if (0 == input.details.token.count)
    364             continue;
    365           break;
    366         }
    367         GNUNET_array_append (choice->inputs,
    368                              choice->inputs_len,
    369                              input);
    370       }
    371     }
    372 
    373     if (NULL != joutputs)
    374     {
    375       const json_t *joutput;
    376       size_t idx;
    377       json_array_foreach ((json_t *) joutputs, idx, joutput)
    378       {
    379         struct TALER_MERCHANT_OrderOutput output = {
    380           .details.token.count = 1
    381         };
    382 
    383         if (GNUNET_OK !=
    384             parse_order_choice_output ((json_t *) joutput,
    385                                        &output,
    386                                        idx))
    387         {
    388           GNUNET_break (0);
    389           return GNUNET_SYSERR;
    390         }
    391         switch (output.type)
    392         {
    393         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
    394           GNUNET_break_op (0);
    395           return GNUNET_SYSERR;
    396         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
    397           /* Ignore output tokens with 'count' field set to 0 */
    398           if (0 == output.details.token.count)
    399             continue;
    400           break;
    401         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
    402           break;
    403         }
    404         GNUNET_array_append (choice->outputs,
    405                              choice->outputs_len,
    406                              output);
    407       }
    408     }
    409   }
    410 
    411   return GNUNET_OK;
    412 }
    413 
    414 
    415 struct GNUNET_JSON_Specification
    416 TALER_MERCHANT_spec_order_choices (
    417   const char *name,
    418   struct TALER_MERCHANT_OrderChoice **choices,
    419   unsigned int *choices_len)
    420 {
    421   struct GNUNET_JSON_Specification ret = {
    422     .cls = (void *) choices_len,
    423     .parser = &parse_order_choices,
    424     .field = name,
    425     .ptr = choices,
    426   };
    427 
    428   return ret;
    429 }
    430 
    431 
    432 void
    433 TALER_MERCHANT_order_choice_free (
    434   struct TALER_MERCHANT_OrderChoice *choice)
    435 {
    436   for (unsigned int i = 0; i < choice->outputs_len; i++)
    437   {
    438     struct TALER_MERCHANT_OrderOutput *output = &choice->outputs[i];
    439 
    440     switch (output->type)
    441     {
    442     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
    443       GNUNET_break (0);
    444       break;
    445     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
    446       break;
    447     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
    448       break;
    449 #if FUTURE
    450     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
    451       GNUNET_free (output->details.coin.exchange_url);
    452       break;
    453 #endif
    454     }
    455   }
    456   GNUNET_free (choice->description);
    457   if (NULL != choice->description_i18n)
    458   {
    459     json_decref (choice->description_i18n);
    460     choice->description_i18n = NULL;
    461   }
    462   GNUNET_free (choice->inputs);
    463   GNUNET_free (choice->outputs);
    464 }