merchant

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

order_parse.c (8362B)


      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_parse.c
     18  * @brief shared logic for order 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 v0-specific fields of @a input JSON into @a order.
     35  *
     36  * @param[in] input the JSON order terms
     37  * @param[out] order where to write the data
     38  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     39  */
     40 static enum GNUNET_GenericReturnValue
     41 parse_order_v0 (
     42   json_t *input,
     43   struct TALER_MERCHANT_Order *order)
     44 {
     45   struct GNUNET_JSON_Specification espec[] = {
     46     TALER_JSON_spec_amount_any ("amount",
     47                                 &order->details.v0.brutto),
     48     GNUNET_JSON_spec_mark_optional (
     49       TALER_JSON_spec_amount_any ("tip",
     50                                   &order->details.v0.tip),
     51       &order->details.v0.no_tip),
     52     GNUNET_JSON_spec_mark_optional (
     53       TALER_JSON_spec_amount_any ("max_fee",
     54                                   &order->details.v0.max_fee),
     55       &order->details.v0.no_max_fee),
     56     GNUNET_JSON_spec_end ()
     57   };
     58   enum GNUNET_GenericReturnValue res;
     59   const char *ename;
     60   unsigned int eline;
     61 
     62   res = GNUNET_JSON_parse (input,
     63                            espec,
     64                            &ename,
     65                            &eline);
     66   if (GNUNET_OK != res)
     67   {
     68     GNUNET_break (0);
     69     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     70                 "Failed to parse order v0 at field %s\n",
     71                 ename);
     72     return GNUNET_SYSERR;
     73   }
     74 
     75   if ( (! order->details.v0.no_max_fee) &&
     76        (GNUNET_OK !=
     77         TALER_amount_cmp_currency (&order->details.v0.max_fee,
     78                                    &order->details.v0.brutto)) )
     79   {
     80     GNUNET_break (0);
     81     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     82                 "'max_fee' does not match currency of order price");
     83     return GNUNET_SYSERR;
     84   }
     85   if ( (! order->details.v0.no_tip) &&
     86        (GNUNET_OK !=
     87         TALER_amount_cmp_currency (&order->details.v0.tip,
     88                                    &order->details.v0.brutto)) )
     89   {
     90     GNUNET_break (0);
     91     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     92                 "'tip' does not match currency of order price");
     93     return GNUNET_SYSERR;
     94   }
     95 
     96   return res;
     97 }
     98 
     99 
    100 /**
    101  * Parse v1-specific fields of @a input JSON into @a order.
    102  *
    103  * @param[in] input the JSON order terms
    104  * @param[out] order where to write the data
    105  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    106  */
    107 static enum GNUNET_GenericReturnValue
    108 parse_order_v1 (
    109   json_t *input,
    110   struct TALER_MERCHANT_Order *order)
    111 {
    112   struct GNUNET_JSON_Specification espec[] = {
    113     TALER_MERCHANT_spec_order_choices (
    114       "choices",
    115       &order->details.v1.choices,
    116       &order->details.v1.choices_len),
    117     GNUNET_JSON_spec_end ()
    118   };
    119 
    120   enum GNUNET_GenericReturnValue res;
    121   const char *ename;
    122   unsigned int eline;
    123 
    124   res = GNUNET_JSON_parse (input,
    125                            espec,
    126                            &ename,
    127                            &eline);
    128   if (GNUNET_OK != res)
    129   {
    130     GNUNET_break (0);
    131     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    132                 "Failed to parse order v1 at field %s\n",
    133                 ename);
    134     return GNUNET_SYSERR;
    135   }
    136 
    137   return res;
    138 }
    139 
    140 
    141 struct TALER_MERCHANT_Order *
    142 TALER_MERCHANT_order_parse (json_t *input)
    143 {
    144   struct TALER_MERCHANT_Order *order
    145     = GNUNET_new (struct TALER_MERCHANT_Order);
    146   enum GNUNET_GenericReturnValue res
    147     = GNUNET_SYSERR;
    148 
    149   GNUNET_assert (NULL != input);
    150   order->base = TALER_MERCHANT_base_terms_parse (input);
    151   if (NULL == order->base)
    152   {
    153     GNUNET_break (0);
    154     GNUNET_free (order);
    155     return NULL;
    156   }
    157   order->refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
    158   order->wire_transfer_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
    159   {
    160     const json_t *products;
    161     struct GNUNET_JSON_Specification espec[] = {
    162       GNUNET_JSON_spec_mark_optional (
    163         GNUNET_JSON_spec_array_const ("products",
    164                                       &products),
    165         NULL),
    166       GNUNET_JSON_spec_mark_optional (
    167         GNUNET_JSON_spec_string_copy ("order_id",
    168                                       &order->order_id),
    169         NULL),
    170       GNUNET_JSON_spec_mark_optional (
    171         GNUNET_JSON_spec_timestamp ("timestamp",
    172                                     &order->timestamp),
    173         NULL),
    174       GNUNET_JSON_spec_mark_optional (
    175         GNUNET_JSON_spec_timestamp ("refund_deadline",
    176                                     &order->refund_deadline),
    177         NULL),
    178       GNUNET_JSON_spec_mark_optional (
    179         GNUNET_JSON_spec_timestamp ("pay_deadline",
    180                                     &order->pay_deadline),
    181         NULL),
    182       GNUNET_JSON_spec_mark_optional (
    183         GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    184                                     &order->wire_transfer_deadline),
    185         NULL),
    186       GNUNET_JSON_spec_end ()
    187     };
    188     const char *ename;
    189     unsigned int eline;
    190 
    191     res = GNUNET_JSON_parse (input,
    192                              espec,
    193                              &ename,
    194                              &eline);
    195     if (GNUNET_OK != res)
    196     {
    197       GNUNET_break_op (0);
    198       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    199                   "Failed to parse proto contract at field %s\n",
    200                   ename);
    201       goto cleanup;
    202     }
    203     if (NULL != products)
    204     {
    205       order->products_len = json_array_size (products);
    206       if (0 != order->products_len)
    207       {
    208         size_t i;
    209         json_t *p;
    210 
    211         order->products = GNUNET_new_array (
    212           order->products_len,
    213           struct TALER_MERCHANT_ProductSold);
    214         json_array_foreach (products, i, p)
    215         {
    216           if (GNUNET_OK !=
    217               TALER_MERCHANT_parse_product_sold (p,
    218                                                  &order->products[i],
    219                                                  true))
    220           {
    221             GNUNET_break (0);
    222             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    223                         "Failed to parse product at offset %u\n",
    224                         (unsigned int) i);
    225             goto cleanup;
    226           }
    227         }
    228       }
    229     }
    230   }
    231   switch (order->base->version)
    232   {
    233   case TALER_MERCHANT_CONTRACT_VERSION_0:
    234     res = parse_order_v0 (input,
    235                           order);
    236     break;
    237   case TALER_MERCHANT_CONTRACT_VERSION_1:
    238     res = parse_order_v1 (input,
    239                           order);
    240     break;
    241   }
    242 
    243   if (GNUNET_OK != res)
    244   {
    245     GNUNET_break (0);
    246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    247                 "Failed to parse order\n");
    248     goto cleanup;
    249   }
    250   return order;
    251 
    252 cleanup:
    253   TALER_MERCHANT_order_free (order);
    254   return NULL;
    255 }
    256 
    257 
    258 void
    259 TALER_MERCHANT_order_free (
    260   struct TALER_MERCHANT_Order *order)
    261 {
    262   if (NULL == order)
    263     return;
    264   if (NULL != order->products)
    265   {
    266     for (size_t i = 0; i<order->products_len; i++)
    267       TALER_MERCHANT_product_sold_free (&order->products[i]);
    268     GNUNET_free (order->products);
    269     order->products_len = 0;
    270   }
    271   GNUNET_free (order->order_id);
    272   if (NULL != order->base)
    273   {
    274     switch (order->base->version)
    275     {
    276     case TALER_MERCHANT_CONTRACT_VERSION_0:
    277       break;
    278     case TALER_MERCHANT_CONTRACT_VERSION_1:
    279       for (unsigned int i = 0;
    280            i < order->details.v1.choices_len;
    281            i++)
    282         TALER_MERCHANT_order_choice_free (&order->details.v1.choices[i]);
    283       GNUNET_free (order->details.v1.choices);
    284       break;
    285     }
    286     TALER_MERCHANT_base_terms_free (order->base);
    287     order->base = NULL;
    288   }
    289   GNUNET_free (order);
    290 }