merchant

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

order_parse.c (10488B)


      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   if (NULL != order->base->amount_external)
     96   {
     97     if (! TALER_MERCHANT_amount_external_valid (
     98           order->base->amount_external))
     99     {
    100       GNUNET_break_op (0);
    101       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    102                   "'amount_external' is not a valid set of external payments");
    103       return GNUNET_SYSERR;
    104     }
    105     if (! TALER_MERCHANT_amount_external_currency_valid (
    106           order->base->amount_external,
    107           &order->details.v0.brutto))
    108     {
    109       GNUNET_break_op (0);
    110       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    111                   "'amount_external' does not match currency of order price");
    112       return GNUNET_SYSERR;
    113     }
    114     if (! TALER_MERCHANT_amount_external_total_valid (
    115           order->base->amount_external,
    116           &order->details.v0.brutto))
    117     {
    118       GNUNET_break_op (0);
    119       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    120                   "total of 'amount_external' and order price is too large");
    121       return GNUNET_SYSERR;
    122     }
    123   }
    124 
    125   return res;
    126 }
    127 
    128 
    129 /**
    130  * Parse v1-specific fields of @a input JSON into @a order.
    131  *
    132  * @param[in] input the JSON order terms
    133  * @param[out] order where to write the data
    134  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    135  */
    136 static enum GNUNET_GenericReturnValue
    137 parse_order_v1 (
    138   json_t *input,
    139   struct TALER_MERCHANT_Order *order)
    140 {
    141   struct GNUNET_JSON_Specification espec[] = {
    142     TALER_MERCHANT_spec_order_choices (
    143       "choices",
    144       &order->details.v1.choices,
    145       &order->details.v1.choices_len),
    146     GNUNET_JSON_spec_end ()
    147   };
    148 
    149   enum GNUNET_GenericReturnValue res;
    150   const char *ename;
    151   unsigned int eline;
    152 
    153   res = GNUNET_JSON_parse (input,
    154                            espec,
    155                            &ename,
    156                            &eline);
    157   if (GNUNET_OK != res)
    158   {
    159     GNUNET_break (0);
    160     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    161                 "Failed to parse order v1 at field %s\n",
    162                 ename);
    163     return GNUNET_SYSERR;
    164   }
    165   if (NULL != order->base->amount_external)
    166   {
    167     if (! TALER_MERCHANT_amount_external_valid (order->base->amount_external))
    168     {
    169       GNUNET_break_op (0);
    170       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    171                   "'amount_external' is not a valid set of external payments\n");
    172       return GNUNET_SYSERR;
    173     }
    174     for (unsigned int i = 0; i<order->details.v1.choices_len; i++)
    175     {
    176       if (! TALER_MERCHANT_amount_external_currency_valid (
    177             order->base->amount_external,
    178             &order->details.v1.choices[i].amount))
    179       {
    180         GNUNET_break_op (0);
    181         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    182                     "'amount_external' does not match currency of choice #%u\n",
    183                     i);
    184         return GNUNET_SYSERR;
    185       }
    186       if (! TALER_MERCHANT_amount_external_total_valid (
    187             order->base->amount_external,
    188             &order->details.v1.choices[i].amount))
    189       {
    190         GNUNET_break_op (0);
    191         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    192                     "total of 'amount_external' and choice #%u is too large\n",
    193                     i);
    194         return GNUNET_SYSERR;
    195       }
    196     }
    197   }
    198 
    199   return res;
    200 }
    201 
    202 
    203 struct TALER_MERCHANT_Order *
    204 TALER_MERCHANT_order_parse (json_t *input)
    205 {
    206   struct TALER_MERCHANT_Order *order
    207     = GNUNET_new (struct TALER_MERCHANT_Order);
    208   enum GNUNET_GenericReturnValue res
    209     = GNUNET_SYSERR;
    210 
    211   GNUNET_assert (NULL != input);
    212   order->base = TALER_MERCHANT_base_terms_parse (input);
    213   if (NULL == order->base)
    214   {
    215     GNUNET_break (0);
    216     GNUNET_free (order);
    217     return NULL;
    218   }
    219   order->refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
    220   order->wire_transfer_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
    221   {
    222     const json_t *products;
    223     struct GNUNET_JSON_Specification espec[] = {
    224       GNUNET_JSON_spec_mark_optional (
    225         GNUNET_JSON_spec_array_const ("products",
    226                                       &products),
    227         NULL),
    228       GNUNET_JSON_spec_mark_optional (
    229         TALER_JSON_spec_slug_copy ("order_id",
    230                                    &order->order_id),
    231         NULL),
    232       GNUNET_JSON_spec_mark_optional (
    233         GNUNET_JSON_spec_timestamp ("timestamp",
    234                                     &order->timestamp),
    235         NULL),
    236       GNUNET_JSON_spec_mark_optional (
    237         GNUNET_JSON_spec_timestamp ("refund_deadline",
    238                                     &order->refund_deadline),
    239         NULL),
    240       GNUNET_JSON_spec_mark_optional (
    241         GNUNET_JSON_spec_timestamp ("pay_deadline",
    242                                     &order->pay_deadline),
    243         NULL),
    244       GNUNET_JSON_spec_mark_optional (
    245         GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    246                                     &order->wire_transfer_deadline),
    247         NULL),
    248       GNUNET_JSON_spec_end ()
    249     };
    250     const char *ename;
    251     unsigned int eline;
    252 
    253     res = GNUNET_JSON_parse (input,
    254                              espec,
    255                              &ename,
    256                              &eline);
    257     if (GNUNET_OK != res)
    258     {
    259       GNUNET_break_op (0);
    260       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    261                   "Failed to parse proto contract at field %s\n",
    262                   ename);
    263       goto cleanup;
    264     }
    265     if (NULL != products)
    266     {
    267       order->products_len = json_array_size (products);
    268       if (0 != order->products_len)
    269       {
    270         size_t i;
    271         json_t *p;
    272 
    273         order->products = GNUNET_new_array (
    274           order->products_len,
    275           struct TALER_MERCHANT_ProductSold);
    276         json_array_foreach (products, i, p)
    277         {
    278           if (GNUNET_OK !=
    279               TALER_MERCHANT_parse_product_sold (p,
    280                                                  &order->products[i],
    281                                                  true))
    282           {
    283             GNUNET_break (0);
    284             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    285                         "Failed to parse product at offset %u\n",
    286                         (unsigned int) i);
    287             goto cleanup;
    288           }
    289         }
    290       }
    291     }
    292   }
    293   switch (order->base->version)
    294   {
    295   case TALER_MERCHANT_CONTRACT_VERSION_0:
    296     res = parse_order_v0 (input,
    297                           order);
    298     break;
    299   case TALER_MERCHANT_CONTRACT_VERSION_1:
    300     res = parse_order_v1 (input,
    301                           order);
    302     break;
    303   }
    304 
    305   if (GNUNET_OK != res)
    306   {
    307     GNUNET_break (0);
    308     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    309                 "Failed to parse order\n");
    310     goto cleanup;
    311   }
    312   return order;
    313 
    314 cleanup:
    315   TALER_MERCHANT_order_free (order);
    316   return NULL;
    317 }
    318 
    319 
    320 void
    321 TALER_MERCHANT_order_free (
    322   struct TALER_MERCHANT_Order *order)
    323 {
    324   if (NULL == order)
    325     return;
    326   if (NULL != order->products)
    327   {
    328     for (size_t i = 0; i<order->products_len; i++)
    329       TALER_MERCHANT_product_sold_free (&order->products[i]);
    330     GNUNET_free (order->products);
    331     order->products_len = 0;
    332   }
    333   GNUNET_free (order->order_id);
    334   if (NULL != order->base)
    335   {
    336     switch (order->base->version)
    337     {
    338     case TALER_MERCHANT_CONTRACT_VERSION_0:
    339       break;
    340     case TALER_MERCHANT_CONTRACT_VERSION_1:
    341       for (unsigned int i = 0;
    342            i < order->details.v1.choices_len;
    343            i++)
    344         TALER_MERCHANT_order_choice_free (&order->details.v1.choices[i]);
    345       GNUNET_free (order->details.v1.choices);
    346       break;
    347     }
    348     TALER_MERCHANT_base_terms_free (order->base);
    349     order->base = NULL;
    350   }
    351   GNUNET_free (order);
    352 }