taler-merchant-httpd_post-using-templates.c (8688B)
1 /* 2 This file is part of TALER 3 (C) 2022-2023 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file taler-merchant-httpd_post-using-templates.c 22 * @brief implementing POST /using-templates request handling 23 * @author Priscilla HUANG 24 * @author Christian Grothoff 25 */ 26 #include "platform.h" 27 #include "taler-merchant-httpd_post-using-templates.h" 28 #include "taler-merchant-httpd_private-post-orders.h" 29 #include "taler-merchant-httpd_helper.h" 30 #include <taler/taler_json_lib.h> 31 32 33 /** 34 * Our context. 35 */ 36 struct UseContext 37 { 38 /** 39 * Internal handler context we are passing into the 40 * POST /private/orders handler. 41 */ 42 struct TMH_HandlerContext ihc; 43 44 /** 45 * Our template details from the DB. 46 */ 47 struct TALER_MERCHANTDB_TemplateDetails etp; 48 49 /** 50 * True once @e etp was initialized. 51 */ 52 bool have_etp; 53 }; 54 55 56 /** 57 * Clean up a `struct UseContext *` 58 * 59 * @param cls a `struct UseContext *` 60 */ 61 static void 62 cleanup_use_context (void *cls) 63 { 64 struct UseContext *uc = cls; 65 66 TALER_MERCHANTDB_template_details_free (&uc->etp); 67 if (NULL != uc->ihc.cc) 68 uc->ihc.cc (uc->ihc.ctx); 69 json_decref (uc->ihc.request_body); 70 GNUNET_free (uc); 71 } 72 73 74 MHD_RESULT 75 TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, 76 struct MHD_Connection *connection, 77 struct TMH_HandlerContext *hc) 78 { 79 struct TMH_MerchantInstance *mi = hc->instance; 80 const char *template_id = hc->infix; 81 const char *summary = NULL; 82 const char *fulfillment_url = NULL; 83 const char *fulfillment_message = NULL; 84 struct TALER_Amount amount; 85 bool no_amount; 86 bool no_summary; 87 struct GNUNET_JSON_Specification spec[] = { 88 GNUNET_JSON_spec_mark_optional ( 89 GNUNET_JSON_spec_string ("summary", 90 &summary), 91 NULL), 92 GNUNET_JSON_spec_mark_optional ( 93 TALER_JSON_spec_amount_any ("amount", 94 &amount), 95 &no_amount), 96 GNUNET_JSON_spec_end () 97 }; 98 struct UseContext *uc = hc->ctx; 99 100 if (NULL == uc) 101 { 102 uc = GNUNET_new (struct UseContext); 103 hc->ctx = uc; 104 hc->cc = &cleanup_use_context; 105 uc->ihc.instance = hc->instance; 106 } 107 108 { 109 enum GNUNET_GenericReturnValue res; 110 111 res = TALER_MHD_parse_json_data (connection, 112 hc->request_body, 113 spec); 114 if (GNUNET_OK != res) 115 { 116 GNUNET_break_op (0); 117 return (GNUNET_NO == res) 118 ? MHD_YES 119 : MHD_NO; 120 } 121 } 122 123 if (! uc->have_etp) 124 { 125 enum GNUNET_DB_QueryStatus qs; 126 127 qs = TMH_db->lookup_template (TMH_db->cls, 128 mi->settings.id, 129 template_id, 130 &uc->etp); 131 switch (qs) 132 { 133 case GNUNET_DB_STATUS_HARD_ERROR: 134 /* Clean up and fail hard */ 135 GNUNET_break (0); 136 GNUNET_JSON_parse_free (spec); 137 return TALER_MHD_reply_with_error ( 138 connection, 139 MHD_HTTP_INTERNAL_SERVER_ERROR, 140 TALER_EC_GENERIC_DB_FETCH_FAILED, 141 NULL); 142 case GNUNET_DB_STATUS_SOFT_ERROR: 143 /* this should be impossible (single select) */ 144 GNUNET_break (0); 145 GNUNET_JSON_parse_free (spec); 146 return TALER_MHD_reply_with_error ( 147 connection, 148 MHD_HTTP_INTERNAL_SERVER_ERROR, 149 TALER_EC_GENERIC_DB_FETCH_FAILED, 150 NULL); 151 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 152 /* template not found! */ 153 GNUNET_JSON_parse_free (spec); 154 return TALER_MHD_reply_with_error ( 155 connection, 156 MHD_HTTP_NOT_FOUND, 157 TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, 158 template_id); 159 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 160 /* all good */ 161 uc->have_etp = true; 162 break; 163 } /* End of the switch */ 164 } 165 if (NULL == uc->ihc.request_body) 166 { 167 /* template */ 168 const char *tsummary = NULL; 169 const char *tcurrency = NULL; 170 uint32_t min_age; 171 struct GNUNET_TIME_Relative pay_duration; 172 struct TALER_Amount tamount; 173 bool no_tamount; 174 struct GNUNET_JSON_Specification tspec[] = { 175 GNUNET_JSON_spec_mark_optional ( 176 GNUNET_JSON_spec_string ("summary", 177 &tsummary), 178 NULL), 179 GNUNET_JSON_spec_mark_optional ( 180 GNUNET_JSON_spec_string ("currency", 181 &tcurrency), 182 NULL), 183 GNUNET_JSON_spec_mark_optional ( 184 TALER_JSON_spec_amount_any ("amount", 185 &tamount), 186 &no_tamount), 187 GNUNET_JSON_spec_uint32 ("minimum_age", 188 &min_age), 189 GNUNET_JSON_spec_relative_time ("pay_duration", 190 &pay_duration), 191 GNUNET_JSON_spec_end () 192 }; 193 json_t *fake_body; 194 195 { 196 enum GNUNET_GenericReturnValue res; 197 const char *err_name; 198 unsigned int err_line; 199 200 res = GNUNET_JSON_parse (uc->etp.template_contract, 201 tspec, 202 &err_name, 203 &err_line); 204 if (GNUNET_OK != res) 205 { 206 GNUNET_break (0); 207 json_dumpf (uc->etp.template_contract, 208 stderr, 209 JSON_INDENT (2)); 210 GNUNET_JSON_parse_free (spec); 211 return TALER_MHD_reply_with_error ( 212 connection, 213 MHD_HTTP_INTERNAL_SERVER_ERROR, 214 TALER_EC_GENERIC_DB_FETCH_FAILED, 215 err_name); 216 } 217 } 218 219 if ( (! no_amount) && 220 (! no_tamount) ) 221 { 222 GNUNET_JSON_parse_free (spec); 223 return TALER_MHD_reply_with_error ( 224 connection, 225 MHD_HTTP_CONFLICT, 226 TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT, 227 NULL); 228 } 229 230 if ( (! no_amount) && 231 (NULL != tcurrency) && 232 (0 != strcmp (tcurrency, 233 amount.currency)) ) 234 { 235 GNUNET_JSON_parse_free (spec); 236 return TALER_MHD_reply_with_error ( 237 connection, 238 MHD_HTTP_CONFLICT, 239 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, 240 tcurrency); 241 } 242 243 if (no_amount && no_tamount) 244 { 245 GNUNET_JSON_parse_free (spec); 246 return TALER_MHD_reply_with_error ( 247 connection, 248 MHD_HTTP_CONFLICT, 249 TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT, 250 NULL); 251 } 252 253 if ( (NULL != summary) && 254 (NULL != tsummary) ) 255 { 256 GNUNET_JSON_parse_free (spec); 257 return TALER_MHD_reply_with_error ( 258 connection, 259 MHD_HTTP_CONFLICT, 260 TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT, 261 NULL); 262 } 263 264 if ( (NULL == summary) && 265 (NULL == tsummary) ) 266 { 267 GNUNET_JSON_parse_free (spec); 268 return TALER_MHD_reply_with_error ( 269 connection, 270 MHD_HTTP_CONFLICT, 271 TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY, 272 NULL); 273 } 274 no_summary = (NULL == summary); 275 fake_body = GNUNET_JSON_PACK ( 276 GNUNET_JSON_pack_allow_null ( 277 GNUNET_JSON_pack_string ("otp_id", 278 uc->etp.otp_id)), 279 GNUNET_JSON_pack_object_steal ( 280 "order", 281 GNUNET_JSON_PACK ( 282 TALER_JSON_pack_amount ("amount", 283 no_amount ? 284 &tamount : 285 &amount), 286 GNUNET_JSON_pack_string ("summary", 287 no_summary ? 288 tsummary : 289 summary), 290 GNUNET_JSON_pack_allow_null ( 291 GNUNET_JSON_pack_string ( 292 "fulfillment_url", 293 fulfillment_url)), 294 GNUNET_JSON_pack_allow_null ( 295 GNUNET_JSON_pack_string ( 296 "fulfillment_message", 297 fulfillment_message)) 298 )) 299 ); 300 uc->ihc.request_body = fake_body; 301 } 302 303 return TMH_private_post_orders ( 304 NULL, /* not even used */ 305 connection, 306 &uc->ihc); 307 }