testing_api_cmd_abort_order.c (11741B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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 General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (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, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing_api_cmd_abort_order.c 21 * @brief command to test the abort feature. 22 * @author Marcello Stanisci 23 */ 24 #include "taler/platform.h" 25 #include <taler/taler_exchange_service.h> 26 #include <taler/taler_testing_lib.h> 27 #include <taler/taler_signatures.h> 28 #include "taler/taler_merchant_service.h" 29 #include "taler/taler_merchant_testing_lib.h" 30 #include <taler/taler-merchant/post-orders-ORDER_ID-abort.h> 31 32 /** 33 * State for a " abort" CMD. 34 */ 35 struct AbortState 36 { 37 38 /** 39 * Reference to the "pay" command to abort. 40 */ 41 const char *pay_reference; 42 43 /** 44 * Merchant URL. 45 */ 46 const char *merchant_url; 47 48 /** 49 * Handle to a "abort" operation. 50 */ 51 struct TALER_MERCHANT_PostOrdersAbortHandle *oah; 52 53 /** 54 * Interpreter state. 55 */ 56 struct TALER_TESTING_Interpreter *is; 57 58 /** 59 * The actual abort/refund data. 60 */ 61 struct TALER_MERCHANT_PostOrdersAbortedCoin *acs; 62 63 /** 64 * Expected HTTP response code. 65 */ 66 unsigned int http_status; 67 68 /** 69 * How many refund permissions this CMD got 70 * the right for. Roughly, there is one refund 71 * permission for one coin. 72 */ 73 unsigned int acs_length; 74 75 }; 76 77 78 /** 79 * Parse the @a coins specification and grow the @a ac 80 * array with the coins found, updating @a nac. 81 * 82 * @param[in,out] ac pointer to array of coins found 83 * @param[in,out] nac length of array at @a pc 84 * @param[in] coins string specifying coins to add to @a pc, 85 * clobbered in the process 86 * @param is interpreter state 87 * @return #GNUNET_OK on success 88 */ 89 static enum GNUNET_GenericReturnValue 90 build_coins (struct TALER_MERCHANT_PostOrdersAbortCoin **ac, 91 unsigned int *nac, 92 char *coins, 93 struct TALER_TESTING_Interpreter *is) 94 { 95 for (char *token = strtok (coins, ";"); 96 NULL != token; 97 token = strtok (NULL, ";")) 98 { 99 char *ctok; 100 unsigned int ci; 101 struct TALER_MERCHANT_PostOrdersAbortCoin *icoin; 102 103 /* Token syntax is "LABEL[/NUMBER]" */ 104 ctok = strchr (token, '/'); 105 ci = 0; 106 if (NULL != ctok) 107 { 108 *ctok = '\0'; 109 ctok++; 110 if (1 != sscanf (ctok, 111 "%u", 112 &ci)) 113 { 114 GNUNET_break (0); 115 return GNUNET_SYSERR; 116 } 117 } 118 { 119 const struct TALER_TESTING_Command *coin_cmd; 120 121 coin_cmd = TALER_TESTING_interpreter_lookup_command (is, 122 token); 123 if (NULL == coin_cmd) 124 { 125 GNUNET_break (0); 126 return GNUNET_SYSERR; 127 } 128 GNUNET_array_grow (*ac, 129 *nac, 130 (*nac) + 1); 131 icoin = &((*ac)[(*nac) - 1]); 132 133 { 134 const struct TALER_CoinSpendPrivateKeyP *coin_priv; 135 136 GNUNET_assert (GNUNET_OK == 137 TALER_TESTING_get_trait_coin_priv (coin_cmd, 138 ci, 139 &coin_priv)); 140 GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, 141 &icoin->coin_pub.eddsa_pub); 142 } 143 GNUNET_assert (GNUNET_OK == 144 TALER_TESTING_get_trait_exchange_url (coin_cmd, 145 &icoin->exchange_url) 146 ); 147 { 148 const struct TALER_Amount *denom_value; 149 150 GNUNET_assert (GNUNET_OK == 151 TALER_TESTING_get_trait_amount (coin_cmd, 152 &denom_value)); 153 icoin->amount_with_fee = *denom_value; 154 } 155 156 } 157 } 158 return GNUNET_OK; 159 } 160 161 162 /** 163 * Callback for a "pay abort" operation. Mainly, check HTTP 164 * response code was as expected and stores refund permissions 165 * in the state. 166 * 167 * @param cls closure. 168 * @param ar response 169 */ 170 static void 171 abort_cb (void *cls, 172 const struct TALER_MERCHANT_PostOrdersAbortResponse *ar) 173 { 174 struct AbortState *as = cls; 175 176 as->oah = NULL; 177 if (as->http_status != ar->hr.http_status) 178 { 179 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 180 "Unexpected response code %u (%d) to command `%s' (expected %u)\n", 181 ar->hr.http_status, 182 (int) ar->hr.ec, 183 TALER_TESTING_interpreter_get_current_label (as->is), 184 as->http_status); 185 TALER_TESTING_FAIL (as->is); 186 } 187 if ( (MHD_HTTP_OK == ar->hr.http_status) && 188 (TALER_EC_NONE == ar->hr.ec) ) 189 { 190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 191 "Received %u refunds\n", 192 ar->details.ok.num_aborts); 193 as->acs_length = ar->details.ok.num_aborts; 194 as->acs = GNUNET_new_array (as->acs_length, 195 struct TALER_MERCHANT_PostOrdersAbortedCoin); 196 GNUNET_memcpy (as->acs, 197 ar->details.ok.aborts, 198 as->acs_length 199 * sizeof (struct TALER_MERCHANT_PostOrdersAbortedCoin)); 200 } 201 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 202 "Successful pay-abort (HTTP status: %u)\n", 203 ar->hr.http_status); 204 TALER_TESTING_interpreter_next (as->is); 205 } 206 207 208 /** 209 * Run an "abort" CMD. 210 * 211 * @param cls closure 212 * @param cmd command being run. 213 * @param is interpreter state 214 */ 215 static void 216 abort_run (void *cls, 217 const struct TALER_TESTING_Command *cmd, 218 struct TALER_TESTING_Interpreter *is) 219 { 220 struct AbortState *as = cls; 221 const struct TALER_TESTING_Command *pay_cmd; 222 const char *proposal_reference; 223 const char *coin_reference; 224 const struct TALER_TESTING_Command *proposal_cmd; 225 const char *order_id; 226 const struct TALER_PrivateContractHashP *h_proposal; 227 struct TALER_MerchantPublicKeyP merchant_pub; 228 struct TALER_Amount total_amount; 229 const char *error_name; 230 unsigned int error_line; 231 struct TALER_MERCHANT_PostOrdersAbortCoin *abort_coins; 232 unsigned int nabort_coins; 233 char *cr; 234 235 as->is = is; 236 pay_cmd = TALER_TESTING_interpreter_lookup_command (is, 237 as->pay_reference); 238 if (NULL == pay_cmd) 239 TALER_TESTING_FAIL (is); 240 if (GNUNET_OK != 241 TALER_TESTING_get_trait_proposal_reference (pay_cmd, 242 &proposal_reference)) 243 TALER_TESTING_FAIL (is); 244 if (GNUNET_OK != 245 TALER_TESTING_get_trait_coin_reference (pay_cmd, 246 0, 247 &coin_reference)) 248 TALER_TESTING_FAIL (is); 249 proposal_cmd = TALER_TESTING_interpreter_lookup_command (is, 250 proposal_reference); 251 252 if (NULL == proposal_cmd) 253 TALER_TESTING_FAIL (is); 254 255 { 256 const json_t *contract_terms; 257 258 if (GNUNET_OK != 259 TALER_TESTING_get_trait_contract_terms (proposal_cmd, 260 &contract_terms)) 261 TALER_TESTING_FAIL (is); 262 { 263 /* Get information that needs to be put verbatim in the 264 * deposit permission */ 265 struct GNUNET_JSON_Specification spec[] = { 266 GNUNET_JSON_spec_string ("order_id", 267 &order_id), 268 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 269 &merchant_pub), 270 TALER_JSON_spec_amount_any ("amount", 271 &total_amount), 272 GNUNET_JSON_spec_end () 273 }; 274 275 if (GNUNET_OK != 276 GNUNET_JSON_parse (contract_terms, 277 spec, 278 &error_name, 279 &error_line)) 280 { 281 char *js; 282 283 js = json_dumps (contract_terms, 284 JSON_INDENT (1)); 285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 286 "Parser failed on %s:%u for input `%s'\n", 287 error_name, 288 error_line, 289 js); 290 free (js); 291 TALER_TESTING_FAIL (is); 292 } 293 } 294 } 295 296 cr = GNUNET_strdup (coin_reference); 297 abort_coins = NULL; 298 nabort_coins = 0; 299 if (GNUNET_OK != 300 build_coins (&abort_coins, 301 &nabort_coins, 302 cr, 303 is)) 304 { 305 GNUNET_array_grow (abort_coins, 306 nabort_coins, 307 0); 308 GNUNET_free (cr); 309 TALER_TESTING_FAIL (is); 310 } 311 GNUNET_free (cr); 312 313 if (GNUNET_OK != 314 TALER_TESTING_get_trait_h_contract_terms (proposal_cmd, 315 &h_proposal)) 316 TALER_TESTING_FAIL (is); 317 as->oah = TALER_MERCHANT_post_orders_abort_create ( 318 TALER_TESTING_interpreter_get_context (is), 319 as->merchant_url, 320 order_id, 321 &merchant_pub, 322 h_proposal, 323 nabort_coins, 324 abort_coins); 325 GNUNET_array_grow (abort_coins, 326 nabort_coins, 327 0); 328 { 329 enum TALER_ErrorCode ec; 330 331 ec = TALER_MERCHANT_post_orders_abort_start ( 332 as->oah, 333 &abort_cb, 334 as); 335 GNUNET_assert (TALER_EC_NONE == ec); 336 } 337 } 338 339 340 /** 341 * Free a "pay abort" CMD, and cancel it if need be. 342 * 343 * @param cls closure. 344 * @param cmd command currently being freed. 345 */ 346 static void 347 abort_cleanup (void *cls, 348 const struct TALER_TESTING_Command *cmd) 349 { 350 struct AbortState *as = cls; 351 352 if (NULL != as->oah) 353 { 354 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 355 "Command `%s' did not complete.\n", 356 TALER_TESTING_interpreter_get_current_label ( 357 as->is)); 358 TALER_MERCHANT_post_orders_abort_cancel (as->oah); 359 } 360 GNUNET_array_grow (as->acs, 361 as->acs_length, 362 0); 363 GNUNET_free (as); 364 } 365 366 367 /** 368 * Offer internal data useful to other commands. 369 * 370 * @param cls closure 371 * @param[out] ret result (could be anything) 372 * @param trait name of the trait 373 * @param index index number of the object to extract. 374 * @return #GNUNET_OK on success 375 */ 376 static int 377 abort_traits (void *cls, 378 const void **ret, 379 const char *trait, 380 unsigned int index) 381 { 382 struct AbortState *as = cls; 383 struct TALER_TESTING_Trait traits[] = { 384 TALER_TESTING_trait_end () 385 }; 386 387 (void) as; 388 return TALER_TESTING_get_trait (traits, 389 ret, 390 trait, 391 index); 392 } 393 394 395 struct TALER_TESTING_Command 396 TALER_TESTING_cmd_merchant_order_abort (const char *label, 397 const char *merchant_url, 398 const char *pay_reference, 399 unsigned int http_status) 400 { 401 struct AbortState *as; 402 403 as = GNUNET_new (struct AbortState); 404 as->http_status = http_status; 405 as->pay_reference = pay_reference; 406 as->merchant_url = merchant_url; 407 { 408 struct TALER_TESTING_Command cmd = { 409 .cls = as, 410 .label = label, 411 .run = &abort_run, 412 .cleanup = &abort_cleanup, 413 .traits = &abort_traits 414 }; 415 416 return cmd; 417 } 418 } 419 420 421 /* end of testing_api_cmd_abort_order.c */