merchant

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

product_parse.c (8228B)


      1 /*
      2   This file is part of TALER
      3   (C) 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/util/product_parse.c
     18  * @brief small helpers
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <string.h>
     23 #include "taler/taler_merchant_util.h"
     24 #include <gnunet/gnunet_json_lib.h>
     25 #include <taler/taler_json_lib.h>
     26 
     27 /**
     28  * Parse the given unit quantity string @a s and store the result in @a q.
     29  *
     30  * @param s quantity to parse
     31  * @param[out] q where to store the result
     32  * @return #GNUNET_OK on success
     33  */
     34 static enum GNUNET_GenericReturnValue
     35 parse_unit_quantity (const char *s,
     36                      struct TALER_MERCHANT_ProductQuantity *q)
     37 {
     38   const char *ptr;
     39   uint64_t integer = 0;
     40   uint32_t frac = 0;
     41   unsigned int digits = 0;
     42 
     43   if (NULL == s)
     44   {
     45     GNUNET_break_op (0);
     46     return GNUNET_SYSERR;
     47   }
     48   ptr = s;
     49   if ('\0' == *ptr)
     50   {
     51     GNUNET_break_op (0);
     52     return GNUNET_SYSERR;
     53   }
     54   if ('-' == *ptr)
     55   {
     56     GNUNET_break_op (0);
     57     return GNUNET_SYSERR;
     58   }
     59   if (! isdigit ((unsigned char) *ptr))
     60   {
     61     GNUNET_break_op (0);
     62     return GNUNET_SYSERR;
     63   }
     64   while (isdigit ((unsigned char) *ptr))
     65   {
     66     unsigned int digit = (unsigned int) (*ptr - '0');
     67 
     68     /* We intentionally allow at most INT64_MAX (as -1 has special meanings),
     69        even though the data type would support UINT64_MAX */
     70     if (integer > (INT64_MAX - digit) / 10)
     71     {
     72       GNUNET_break_op (0);
     73       return GNUNET_SYSERR;
     74     }
     75     integer = integer * 10 + digit;
     76     ptr++;
     77   }
     78   if ('.' == *ptr)
     79   {
     80     ptr++;
     81     if ('\0' == *ptr)
     82     {
     83       GNUNET_break_op (0);
     84       return GNUNET_SYSERR;
     85     }
     86     while (isdigit ((unsigned char) *ptr))
     87     {
     88       unsigned int digit = (unsigned int) (*ptr - '0');
     89 
     90       if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
     91       {
     92         GNUNET_break_op (0);
     93         return GNUNET_SYSERR;
     94       }
     95       frac = (uint32_t) (frac * 10 + digit);
     96       digits++;
     97       ptr++;
     98     }
     99     while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
    100     {
    101       frac *= 10;
    102       digits++;
    103     }
    104   }
    105   if ('\0' != *ptr)
    106   {
    107     GNUNET_break_op (0);
    108     return GNUNET_SYSERR;
    109   }
    110   q->integer = integer;
    111   q->fractional = frac;
    112   return GNUNET_OK;
    113 }
    114 
    115 
    116 enum GNUNET_GenericReturnValue
    117 TALER_MERCHANT_parse_product_sold (const json_t *pj,
    118                                    struct TALER_MERCHANT_ProductSold *r,
    119                                    bool allow_mip)
    120 {
    121   // FIXME: properly implement distinction between MIP and non-MIP!
    122   bool no_quantity;
    123   bool no_unit_quantity;
    124   bool no_price;
    125   uint64_t legacy_quantity;
    126   const char *unit_quantity_s;
    127   struct TALER_Amount price;
    128   const json_t *prices = NULL;
    129   struct GNUNET_JSON_Specification spec[] = {
    130     GNUNET_JSON_spec_mark_optional (
    131       GNUNET_JSON_spec_string_copy ("product_id",
    132                                     &r->product_id),
    133       NULL),
    134     GNUNET_JSON_spec_mark_optional (
    135       GNUNET_JSON_spec_string_copy ("product_name",
    136                                     &r->product_name),
    137       NULL),
    138     GNUNET_JSON_spec_mark_optional (
    139       GNUNET_JSON_spec_string_copy ("description",
    140                                     &r->description),
    141       NULL),
    142     GNUNET_JSON_spec_mark_optional (
    143       GNUNET_JSON_spec_object_copy ("description_i18n",
    144                                     &r->description_i18n),
    145       NULL),
    146     GNUNET_JSON_spec_mark_optional (
    147       GNUNET_JSON_spec_uint64 ("quantity",
    148                                &legacy_quantity),
    149       &no_quantity),
    150     GNUNET_JSON_spec_mark_optional (
    151       GNUNET_JSON_spec_bool ("prices_are_net",
    152                              &r->prices_are_net),
    153       NULL),
    154     GNUNET_JSON_spec_mark_optional (
    155       GNUNET_JSON_spec_string ("unit_quantity",
    156                                &unit_quantity_s),
    157       &no_unit_quantity),
    158     GNUNET_JSON_spec_mark_optional (
    159       GNUNET_JSON_spec_string_copy ("unit",
    160                                     &r->unit),
    161       NULL),
    162     GNUNET_JSON_spec_mark_optional (
    163       TALER_JSON_spec_amount_any ("price",
    164                                   &price),
    165       &no_price),
    166     GNUNET_JSON_spec_mark_optional (
    167       GNUNET_JSON_spec_array_const ("prices",
    168                                     &prices),
    169       NULL),
    170     GNUNET_JSON_spec_mark_optional (
    171       GNUNET_JSON_spec_string_copy ("image",
    172                                     &r->image),
    173       NULL),
    174     GNUNET_JSON_spec_mark_optional (
    175       GNUNET_JSON_spec_array_copy ("taxes",
    176                                    &r->taxes),
    177       NULL),
    178     GNUNET_JSON_spec_mark_optional (
    179       GNUNET_JSON_spec_timestamp ("delivery_date",
    180                                   &r->delivery_date),
    181       NULL),
    182     GNUNET_JSON_spec_mark_optional (
    183       GNUNET_JSON_spec_uint64 ("product_money_pot",
    184                                &r->product_money_pot),
    185       NULL),
    186     GNUNET_JSON_spec_end ()
    187   };
    188   enum GNUNET_GenericReturnValue res;
    189   const char *ename;
    190   unsigned int eline;
    191 
    192   r->delivery_date = GNUNET_TIME_UNIT_FOREVER_TS;
    193   res = GNUNET_JSON_parse (pj,
    194                            spec,
    195                            &ename,
    196                            &eline);
    197   if (GNUNET_OK != res)
    198   {
    199     GNUNET_break (0);
    200     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    201                 "Failed to parse product at field %s\n",
    202                 ename);
    203     return GNUNET_SYSERR;
    204   }
    205   if (! no_quantity)
    206   {
    207     r->unit_quantity.integer = legacy_quantity;
    208     r->unit_quantity.fractional = 0;
    209   }
    210   if (! no_unit_quantity)
    211   {
    212     if (GNUNET_OK !=
    213         parse_unit_quantity (unit_quantity_s,
    214                              &r->unit_quantity))
    215     {
    216       GNUNET_break (0);
    217       return GNUNET_SYSERR;
    218     }
    219   }
    220   if ( (! no_quantity) && (! no_unit_quantity) )
    221   {
    222     GNUNET_break ( (0 == r->unit_quantity.fractional) &&
    223                    (legacy_quantity == r->unit_quantity.integer) );
    224   }
    225   if ( (NULL != r->image) &&
    226        (! TALER_MERCHANT_image_data_url_valid (r->image)) )
    227   {
    228     GNUNET_break_op (0);
    229     return GNUNET_SYSERR;
    230   }
    231   if ( (NULL != r->taxes) &&
    232        (! TALER_MERCHANT_taxes_array_valid (r->taxes)) )
    233   {
    234     GNUNET_break_op (0);
    235     return GNUNET_SYSERR;
    236   }
    237   if (NULL != prices)
    238   {
    239     size_t len = json_array_size (prices);
    240     size_t i;
    241     json_t *price_i;
    242 
    243     GNUNET_assert (len < UINT_MAX);
    244     r->prices_length = (unsigned int) len;
    245     r->prices = GNUNET_new_array (r->prices_length,
    246                                   struct TALER_Amount);
    247     json_array_foreach (prices, i, price_i)
    248     {
    249       struct GNUNET_JSON_Specification pspec[] = {
    250         TALER_JSON_spec_amount_any (NULL,
    251                                     &r->prices[i]),
    252         GNUNET_JSON_spec_end ()
    253       };
    254 
    255       res = GNUNET_JSON_parse (price_i,
    256                                pspec,
    257                                &ename,
    258                                &eline);
    259       if (GNUNET_OK != res)
    260       {
    261         GNUNET_break (0);
    262         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    263                     "Failed to parse price at index %u\n",
    264                     (unsigned int) i);
    265         return GNUNET_SYSERR;
    266       }
    267     }
    268   }
    269   else if (! no_price)
    270   {
    271     r->prices_length = 1;
    272     r->prices = GNUNET_new_array (1,
    273                                   struct TALER_Amount);
    274     r->prices[0] = price;
    275   }
    276   return GNUNET_OK;
    277 }
    278 
    279 
    280 void
    281 TALER_MERCHANT_product_sold_free (struct TALER_MERCHANT_ProductSold *product)
    282 {
    283   GNUNET_free (product->product_id);
    284   GNUNET_free (product->product_name);
    285   GNUNET_free (product->description);
    286   json_decref (product->description_i18n);
    287   GNUNET_free (product->prices);
    288   GNUNET_free (product->unit);
    289   GNUNET_free (product->image);
    290   json_decref (product->taxes);
    291 }