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 */