merchant

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

merchant_api_post-private-products-PRODUCT_ID-lock.c (9028B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020-2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Lesser General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General
     16   Public License along with TALER; see the file COPYING.LGPL.
     17   If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file merchant_api_post-private-products-PRODUCT_ID-lock-new.c
     21  * @brief Implementation of the POST /private/products/$PRODUCT_ID/lock request
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/platform.h"
     25 #include <curl/curl.h>
     26 #include <jansson.h>
     27 #include <microhttpd.h> /* just for HTTP status codes */
     28 #include <gnunet/gnunet_util_lib.h>
     29 #include <gnunet/gnunet_curl_lib.h>
     30 #include <taler/taler-merchant/post-private-products-PRODUCT_ID-lock.h>
     31 #include "merchant_api_curl_defaults.h"
     32 #include "merchant_api_common.h"
     33 #include <taler/taler_json_lib.h>
     34 #include <taler/taler_curl_lib.h>
     35 
     36 
     37 /**
     38  * Handle for a POST /private/products/$PRODUCT_ID/lock operation.
     39  */
     40 struct TALER_MERCHANT_PostPrivateProductsLockHandle
     41 {
     42   /**
     43    * Base URL of the merchant backend.
     44    */
     45   char *base_url;
     46 
     47   /**
     48    * The full URL for this request.
     49    */
     50   char *url;
     51 
     52   /**
     53    * Handle for the request.
     54    */
     55   struct GNUNET_CURL_Job *job;
     56 
     57   /**
     58    * Function to call with the result.
     59    */
     60   TALER_MERCHANT_PostPrivateProductsLockCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_RESULT_CLOSURE *cb_cls;
     66 
     67   /**
     68    * Reference to the execution context.
     69    */
     70   struct GNUNET_CURL_Context *ctx;
     71 
     72   /**
     73    * Minor context that holds body and headers.
     74    */
     75   struct TALER_CURL_PostContext post_ctx;
     76 
     77   /**
     78    * Product identifier.
     79    */
     80   char *product_id;
     81 
     82   /**
     83    * Lock UUID.
     84    */
     85   char *uuid;
     86 
     87   /**
     88    * How long to lock the inventory.
     89    */
     90   struct GNUNET_TIME_Relative duration;
     91 
     92   /**
     93    * Number of units to lock.
     94    */
     95   uint64_t quantity;
     96 
     97   /**
     98    * Fractional part of the quantity.
     99    */
    100   uint32_t quantity_frac;
    101 
    102   /**
    103    * Whether to use fractional quantity.
    104    */
    105   bool use_fractional_quantity;
    106 };
    107 
    108 
    109 /**
    110  * Function called when we're done processing the
    111  * HTTP POST /private/products/$PRODUCT_ID/lock request.
    112  *
    113  * @param cls the `struct TALER_MERCHANT_PostPrivateProductsLockHandle`
    114  * @param response_code HTTP response code, 0 on error
    115  * @param response response body, NULL if not in JSON
    116  */
    117 static void
    118 handle_post_products_lock_finished (void *cls,
    119                                     long response_code,
    120                                     const void *response)
    121 {
    122   struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh = cls;
    123   const json_t *json = response;
    124   struct TALER_MERCHANT_PostPrivateProductsLockResponse plr = {
    125     .hr.http_status = (unsigned int) response_code,
    126     .hr.reply = json
    127   };
    128 
    129   pplh->job = NULL;
    130   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    131               "POST /private/products/$PRODUCT_ID/lock completed with response code %u\n",
    132               (unsigned int) response_code);
    133   switch (response_code)
    134   {
    135   case 0:
    136     plr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    137     break;
    138   case MHD_HTTP_NO_CONTENT:
    139     break;
    140   case MHD_HTTP_UNAUTHORIZED:
    141     plr.hr.ec = TALER_JSON_get_error_code (json);
    142     plr.hr.hint = TALER_JSON_get_error_hint (json);
    143     break;
    144   case MHD_HTTP_BAD_REQUEST:
    145     GNUNET_break_op (0);
    146     plr.hr.ec = TALER_JSON_get_error_code (json);
    147     plr.hr.hint = TALER_JSON_get_error_hint (json);
    148     break;
    149   case MHD_HTTP_FORBIDDEN:
    150     plr.hr.ec = TALER_JSON_get_error_code (json);
    151     plr.hr.hint = TALER_JSON_get_error_hint (json);
    152     break;
    153   case MHD_HTTP_NOT_FOUND:
    154     plr.hr.ec = TALER_JSON_get_error_code (json);
    155     plr.hr.hint = TALER_JSON_get_error_hint (json);
    156     break;
    157   case MHD_HTTP_GONE:
    158     plr.hr.ec = TALER_JSON_get_error_code (json);
    159     plr.hr.hint = TALER_JSON_get_error_hint (json);
    160     break;
    161   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    162     plr.hr.ec = TALER_JSON_get_error_code (json);
    163     plr.hr.hint = TALER_JSON_get_error_hint (json);
    164     break;
    165   default:
    166     TALER_MERCHANT_parse_error_details_ (json,
    167                                          response_code,
    168                                          &plr.hr);
    169     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    170                 "Unexpected response code %u/%d\n",
    171                 (unsigned int) response_code,
    172                 (int) plr.hr.ec);
    173     GNUNET_break_op (0);
    174     break;
    175   }
    176   pplh->cb (pplh->cb_cls,
    177             &plr);
    178   TALER_MERCHANT_post_private_products_lock_cancel (pplh);
    179 }
    180 
    181 
    182 struct TALER_MERCHANT_PostPrivateProductsLockHandle *
    183 TALER_MERCHANT_post_private_products_lock_create (
    184   struct GNUNET_CURL_Context *ctx,
    185   const char *url,
    186   const char *product_id,
    187   const char *uuid,
    188   struct GNUNET_TIME_Relative duration,
    189   uint64_t quantity)
    190 {
    191   struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh;
    192 
    193   pplh = GNUNET_new (struct TALER_MERCHANT_PostPrivateProductsLockHandle);
    194   pplh->ctx = ctx;
    195   pplh->base_url = GNUNET_strdup (url);
    196   pplh->product_id = GNUNET_strdup (product_id);
    197   pplh->uuid = GNUNET_strdup (uuid);
    198   pplh->duration = duration;
    199   pplh->quantity = quantity;
    200   return pplh;
    201 }
    202 
    203 
    204 enum GNUNET_GenericReturnValue
    205 TALER_MERCHANT_post_private_products_lock_set_options_ (
    206   struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh,
    207   unsigned int num_options,
    208   const struct TALER_MERCHANT_PostPrivateProductsLockOptionValue *options)
    209 {
    210   for (unsigned int i = 0; i < num_options; i++)
    211   {
    212     switch (options[i].option)
    213     {
    214     case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_END:
    215       return GNUNET_OK;
    216     case TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_QUANTITY_FRAC:
    217       pplh->quantity_frac = options[i].details.quantity_frac;
    218       break;
    219     case
    220       TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_OPTION_USE_FRACTIONAL_QUANTITY:
    221       pplh->use_fractional_quantity
    222         = options[i].details.use_fractional_quantity;
    223       break;
    224     default:
    225       GNUNET_break (0);
    226       return GNUNET_SYSERR;
    227     }
    228   }
    229   return GNUNET_OK;
    230 }
    231 
    232 
    233 enum TALER_ErrorCode
    234 TALER_MERCHANT_post_private_products_lock_start (
    235   struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh,
    236   TALER_MERCHANT_PostPrivateProductsLockCallback cb,
    237   TALER_MERCHANT_POST_PRIVATE_PRODUCTS_LOCK_RESULT_CLOSURE *cb_cls)
    238 {
    239   json_t *req_obj;
    240   CURL *eh;
    241   char unit_quantity_buf[64];
    242 
    243   pplh->cb = cb;
    244   pplh->cb_cls = cb_cls;
    245   {
    246     char *path;
    247 
    248     GNUNET_asprintf (&path,
    249                      "private/products/%s/lock",
    250                      pplh->product_id);
    251     pplh->url = TALER_url_join (pplh->base_url,
    252                                 path,
    253                                 NULL);
    254     GNUNET_free (path);
    255   }
    256   if (NULL == pplh->url)
    257     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    258   GNUNET_assert ( (0 == pplh->quantity_frac) ||
    259                   pplh->use_fractional_quantity);
    260   TALER_MERCHANT_format_quantity_string (pplh->quantity,
    261                                          pplh->quantity_frac,
    262                                          unit_quantity_buf,
    263                                          sizeof (unit_quantity_buf));
    264   req_obj = GNUNET_JSON_PACK (
    265     GNUNET_JSON_pack_string ("lock_uuid",
    266                              pplh->uuid),
    267     GNUNET_JSON_pack_time_rel ("duration",
    268                                pplh->duration),
    269     GNUNET_JSON_pack_string ("unit_quantity",
    270                              unit_quantity_buf));
    271   eh = TALER_MERCHANT_curl_easy_get_ (pplh->url);
    272   if ( (NULL == eh) ||
    273        (GNUNET_OK !=
    274         TALER_curl_easy_post (&pplh->post_ctx,
    275                               eh,
    276                               req_obj)) )
    277   {
    278     GNUNET_break (0);
    279     json_decref (req_obj);
    280     if (NULL != eh)
    281       curl_easy_cleanup (eh);
    282     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    283   }
    284   json_decref (req_obj);
    285   pplh->job = GNUNET_CURL_job_add2 (pplh->ctx,
    286                                     eh,
    287                                     pplh->post_ctx.headers,
    288                                     &handle_post_products_lock_finished,
    289                                     pplh);
    290   if (NULL == pplh->job)
    291     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    292   return TALER_EC_NONE;
    293 }
    294 
    295 
    296 void
    297 TALER_MERCHANT_post_private_products_lock_cancel (
    298   struct TALER_MERCHANT_PostPrivateProductsLockHandle *pplh)
    299 {
    300   if (NULL != pplh->job)
    301   {
    302     GNUNET_CURL_job_cancel (pplh->job);
    303     pplh->job = NULL;
    304   }
    305   TALER_curl_easy_post_finished (&pplh->post_ctx);
    306   GNUNET_free (pplh->product_id);
    307   GNUNET_free (pplh->uuid);
    308   GNUNET_free (pplh->url);
    309   GNUNET_free (pplh->base_url);
    310   GNUNET_free (pplh);
    311 }
    312 
    313 
    314 /* end of merchant_api_post-private-products-PRODUCT_ID-lock-new.c */