merchant_api_post_units.c (6446B)
1 /* 2 This file is part of TALER 3 Copyright (C) 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 7 Software Foundation; either version 2.1, 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 Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file merchant_api_post_units.c 18 * @brief Implementation of POST /private/units 19 * @author Bohdan Potuzhnyi 20 */ 21 #include "platform.h" 22 #include <curl/curl.h> 23 #include <jansson.h> 24 #include <microhttpd.h> 25 #include <gnunet/gnunet_util_lib.h> 26 #include "taler_merchant_service.h" 27 #include "merchant_api_curl_defaults.h" 28 #include "merchant_api_common.h" 29 #include <taler/taler_json_lib.h> 30 #include <taler/taler_curl_lib.h> 31 32 33 /** 34 * Handle for a POST /private/units operation. 35 */ 36 struct TALER_MERCHANT_UnitsPostHandle 37 { 38 /** 39 * Fully qualified request URL. 40 */ 41 char *url; 42 43 /** 44 * CURL job handle. 45 */ 46 struct GNUNET_CURL_Job *job; 47 48 /** 49 * Callback to invoke with the result. 50 */ 51 TALER_MERCHANT_UnitsPostCallback cb; 52 53 /** 54 * Closure for @a cb. 55 */ 56 void *cb_cls; 57 58 /** 59 * Execution context. 60 */ 61 struct GNUNET_CURL_Context *ctx; 62 63 /** 64 * Helper keeping POST body and headers alive. 65 */ 66 struct TALER_CURL_PostContext post_ctx; 67 }; 68 69 70 /** 71 * Called when the HTTP transfer finishes. 72 * 73 * @param cls operation handle 74 * @param response_code HTTP status (0 on network / parsing failures) 75 * @param response parsed JSON reply (NULL if unavailable) 76 */ 77 static void 78 handle_post_units_finished (void *cls, 79 long response_code, 80 const void *response) 81 { 82 struct TALER_MERCHANT_UnitsPostHandle *uph = cls; 83 const json_t *json = response; 84 struct TALER_MERCHANT_HttpResponse hr = { 85 .http_status = (unsigned int) response_code, 86 .reply = json 87 }; 88 89 uph->job = NULL; 90 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 91 "POST /private/units completed with status %u\n", 92 (unsigned int) response_code); 93 switch (response_code) 94 { 95 case MHD_HTTP_NO_CONTENT: 96 break; 97 case MHD_HTTP_BAD_REQUEST: 98 case MHD_HTTP_UNAUTHORIZED: 99 case MHD_HTTP_FORBIDDEN: 100 case MHD_HTTP_NOT_FOUND: 101 case MHD_HTTP_CONFLICT: 102 case MHD_HTTP_INTERNAL_SERVER_ERROR: 103 hr.ec = TALER_JSON_get_error_code (json); 104 hr.hint = TALER_JSON_get_error_hint (json); 105 break; 106 case 0: 107 hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 108 break; 109 default: 110 TALER_MERCHANT_parse_error_details_ (json, 111 response_code, 112 &hr); 113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 114 "Unexpected response %u/%d for POST /private/units\n", 115 (unsigned int) response_code, 116 (int) hr.ec); 117 GNUNET_break_op (0); 118 break; 119 } 120 uph->cb (uph->cb_cls, 121 &hr); 122 TALER_MERCHANT_units_post_cancel (uph); 123 } 124 125 126 struct TALER_MERCHANT_UnitsPostHandle * 127 TALER_MERCHANT_units_post (struct GNUNET_CURL_Context *ctx, 128 const char *backend_url, 129 const char *unit_id, 130 const char *unit_name_long, 131 const char *unit_name_short, 132 bool unit_allow_fraction, 133 uint32_t unit_precision_level, 134 bool unit_active, 135 const json_t *unit_name_long_i18n, 136 const json_t *unit_name_short_i18n, 137 TALER_MERCHANT_UnitsPostCallback cb, 138 void *cb_cls) 139 { 140 struct TALER_MERCHANT_UnitsPostHandle *uph; 141 json_t *req_obj; 142 143 req_obj = GNUNET_JSON_PACK ( 144 GNUNET_JSON_pack_string ("unit", 145 unit_id), 146 GNUNET_JSON_pack_string ("unit_name_long", 147 unit_name_long), 148 GNUNET_JSON_pack_string ("unit_name_short", 149 unit_name_short), 150 GNUNET_JSON_pack_bool ("unit_allow_fraction", 151 unit_allow_fraction), 152 GNUNET_JSON_pack_uint64 ("unit_precision_level", 153 (uint64_t) unit_precision_level), 154 GNUNET_JSON_pack_bool ("unit_active", 155 unit_active), 156 GNUNET_JSON_pack_allow_null ( 157 GNUNET_JSON_pack_object_incref ("unit_name_long_i18n", 158 (json_t *) unit_name_long_i18n)), 159 GNUNET_JSON_pack_allow_null ( 160 GNUNET_JSON_pack_object_incref ("unit_name_short_i18n", 161 (json_t *) unit_name_short_i18n))); 162 uph = GNUNET_new (struct TALER_MERCHANT_UnitsPostHandle); 163 uph->ctx = ctx; 164 uph->cb = cb; 165 uph->cb_cls = cb_cls; 166 uph->url = TALER_url_join (backend_url, 167 "private/units", 168 NULL); 169 if (NULL == uph->url) 170 { 171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 172 "Failed to build /private/units URL\n"); 173 json_decref (req_obj); 174 GNUNET_free (uph); 175 return NULL; 176 } 177 { 178 CURL *eh; 179 180 eh = TALER_MERCHANT_curl_easy_get_ (uph->url); 181 if (GNUNET_OK != 182 TALER_curl_easy_post (&uph->post_ctx, 183 eh, 184 req_obj)) 185 { 186 GNUNET_break (0); 187 curl_easy_cleanup (eh); 188 json_decref (req_obj); 189 GNUNET_free (uph->url); 190 GNUNET_free (uph); 191 return NULL; 192 } 193 json_decref (req_obj); 194 uph->job = GNUNET_CURL_job_add2 (ctx, 195 eh, 196 uph->post_ctx.headers, 197 &handle_post_units_finished, 198 uph); 199 } 200 return uph; 201 } 202 203 204 void 205 TALER_MERCHANT_units_post_cancel (struct TALER_MERCHANT_UnitsPostHandle *uph) 206 { 207 if (NULL != uph->job) 208 { 209 GNUNET_CURL_job_cancel (uph->job); 210 uph->job = NULL; 211 } 212 TALER_curl_easy_post_finished (&uph->post_ctx); 213 GNUNET_free (uph->url); 214 GNUNET_free (uph); 215 } 216 217 218 /* end of merchant_api_post_units.c */