testing_api_cmd_refund.c (9527B)
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/testing_api_cmd_refund.c 21 * @brief Implement the /refund test command, plus other 22 * corollary commands (?). 23 * @author Marcello Stanisci 24 */ 25 #include "taler/platform.h" 26 #include "taler/taler_json_lib.h" 27 #include <gnunet/gnunet_curl_lib.h> 28 #include "taler/taler_testing_lib.h" 29 30 31 /** 32 * State for a "refund" CMD. 33 */ 34 struct RefundState 35 { 36 /** 37 * Expected HTTP response code. 38 */ 39 unsigned int expected_response_code; 40 41 /** 42 * Amount to be refunded. 43 */ 44 const char *refund_amount; 45 46 /** 47 * Reference to any command that can provide a coin to refund. 48 */ 49 const char *coin_reference; 50 51 /** 52 * Refund transaction identifier. 53 */ 54 uint64_t refund_transaction_id; 55 56 /** 57 * Entry in the coin's history generated by this operation. 58 */ 59 struct TALER_EXCHANGE_CoinHistoryEntry che; 60 61 /** 62 * Public key of the refunded coin. 63 */ 64 struct TALER_CoinSpendPublicKeyP coin; 65 66 /** 67 * Handle to the refund operation. 68 */ 69 struct TALER_EXCHANGE_RefundHandle *rh; 70 71 /** 72 * Interpreter state. 73 */ 74 struct TALER_TESTING_Interpreter *is; 75 }; 76 77 78 /** 79 * Check the result for the refund request, just check if the 80 * response code is acceptable. 81 * 82 * @param cls closure 83 * @param rr response details 84 */ 85 static void 86 refund_cb (void *cls, 87 const struct TALER_EXCHANGE_RefundResponse *rr) 88 { 89 struct RefundState *rs = cls; 90 const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr; 91 92 rs->rh = NULL; 93 if (rs->expected_response_code != hr->http_status) 94 { 95 TALER_TESTING_unexpected_status (rs->is, 96 hr->http_status, 97 rs->expected_response_code); 98 return; 99 } 100 if (MHD_HTTP_OK == hr->http_status) 101 { 102 struct TALER_Amount refund_amount; 103 104 if (GNUNET_OK != 105 TALER_string_to_amount (rs->refund_amount, 106 &refund_amount)) 107 { 108 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 109 "Failed to parse amount `%s'\n", 110 rs->refund_amount); 111 TALER_TESTING_interpreter_fail (rs->is); 112 return; 113 } 114 if (0 > 115 TALER_amount_subtract (&rs->che.amount, 116 &refund_amount, 117 &rs->che.details.refund.refund_fee)) 118 { 119 GNUNET_break (0); 120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 121 "Failed to subtract %s from %s\n", 122 TALER_amount2s (&rs->che.details.refund.refund_fee), 123 rs->refund_amount); 124 TALER_TESTING_interpreter_fail (rs->is); 125 return; 126 } 127 } 128 TALER_TESTING_interpreter_next (rs->is); 129 } 130 131 132 /** 133 * Run the command. 134 * 135 * @param cls closure. 136 * @param cmd the command to execute. 137 * @param is the interpreter state. 138 */ 139 static void 140 refund_run (void *cls, 141 const struct TALER_TESTING_Command *cmd, 142 struct TALER_TESTING_Interpreter *is) 143 { 144 struct RefundState *rs = cls; 145 const struct TALER_CoinSpendPrivateKeyP *coin_priv; 146 const json_t *contract_terms; 147 struct TALER_PrivateContractHashP h_contract_terms; 148 struct TALER_Amount refund_amount; 149 const struct TALER_MerchantPrivateKeyP *merchant_priv; 150 const struct TALER_TESTING_Command *coin_cmd; 151 const struct TALER_EXCHANGE_DenomPublicKey *denom_pub; 152 153 rs->is = is; 154 if (GNUNET_OK != 155 TALER_string_to_amount (rs->refund_amount, 156 &refund_amount)) 157 { 158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 159 "Failed to parse amount `%s' at %s\n", 160 rs->refund_amount, 161 cmd->label); 162 TALER_TESTING_interpreter_fail (is); 163 return; 164 } 165 coin_cmd = TALER_TESTING_interpreter_lookup_command (is, 166 rs->coin_reference); 167 if (NULL == coin_cmd) 168 { 169 GNUNET_break (0); 170 TALER_TESTING_interpreter_fail (is); 171 return; 172 } 173 if (GNUNET_OK != 174 TALER_TESTING_get_trait_contract_terms (coin_cmd, 175 &contract_terms)) 176 { 177 GNUNET_break (0); 178 TALER_TESTING_interpreter_fail (is); 179 return; 180 } 181 GNUNET_assert (GNUNET_OK == 182 TALER_JSON_contract_hash (contract_terms, 183 &h_contract_terms)); 184 185 /* Hunting for a coin .. */ 186 if ( (GNUNET_OK != 187 TALER_TESTING_get_trait_coin_priv (coin_cmd, 188 0, 189 &coin_priv)) || 190 (GNUNET_OK != 191 TALER_TESTING_get_trait_denom_pub (coin_cmd, 192 0, 193 &denom_pub)) ) 194 195 { 196 GNUNET_break (0); 197 TALER_TESTING_interpreter_fail (is); 198 return; 199 } 200 201 GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, 202 &rs->coin.eddsa_pub); 203 if (GNUNET_OK != 204 TALER_TESTING_get_trait_merchant_priv (coin_cmd, 205 &merchant_priv)) 206 { 207 GNUNET_break (0); 208 TALER_TESTING_interpreter_fail (is); 209 return; 210 } 211 rs->che.type = TALER_EXCHANGE_CTT_REFUND; 212 rs->che.details.refund.h_contract_terms = h_contract_terms; 213 GNUNET_CRYPTO_eddsa_key_get_public ( 214 &merchant_priv->eddsa_priv, 215 &rs->che.details.refund.merchant_pub.eddsa_pub); 216 rs->che.details.refund.refund_fee = denom_pub->fees.refund; 217 rs->che.details.refund.sig_amount = refund_amount; 218 rs->che.details.refund.rtransaction_id = rs->refund_transaction_id; 219 TALER_merchant_refund_sign (&rs->coin, 220 &h_contract_terms, 221 rs->refund_transaction_id, 222 &refund_amount, 223 merchant_priv, 224 &rs->che.details.refund.sig); 225 rs->rh = TALER_EXCHANGE_refund ( 226 TALER_TESTING_interpreter_get_context (is), 227 TALER_TESTING_get_exchange_url (is), 228 TALER_TESTING_get_keys (is), 229 &refund_amount, 230 &h_contract_terms, 231 &rs->coin, 232 rs->refund_transaction_id, 233 merchant_priv, 234 &refund_cb, 235 rs); 236 GNUNET_assert (NULL != rs->rh); 237 } 238 239 240 /** 241 * Offer internal data from a "refund" CMD, to other commands. 242 * 243 * @param cls closure. 244 * @param[out] ret result. 245 * @param trait name of the trait. 246 * @param index index number of the object to offer. 247 * @return #GNUNET_OK on success. 248 */ 249 static enum GNUNET_GenericReturnValue 250 refund_traits (void *cls, 251 const void **ret, 252 const char *trait, 253 unsigned int index) 254 { 255 struct RefundState *rs = cls; 256 struct TALER_TESTING_Trait traits[] = { 257 TALER_TESTING_make_trait_coin_history (0, 258 &rs->che), 259 TALER_TESTING_make_trait_coin_pub (0, 260 &rs->coin), 261 TALER_TESTING_trait_end () 262 }; 263 264 return TALER_TESTING_get_trait (traits, 265 ret, 266 trait, 267 index); 268 } 269 270 271 /** 272 * Free the state from a "refund" CMD, and possibly cancel 273 * a pending operation thereof. 274 * 275 * @param cls closure. 276 * @param cmd the command which is being cleaned up. 277 */ 278 static void 279 refund_cleanup (void *cls, 280 const struct TALER_TESTING_Command *cmd) 281 { 282 struct RefundState *rs = cls; 283 284 if (NULL != rs->rh) 285 { 286 TALER_TESTING_command_incomplete (rs->is, 287 cmd->label); 288 TALER_EXCHANGE_refund_cancel (rs->rh); 289 rs->rh = NULL; 290 } 291 GNUNET_free (rs); 292 } 293 294 295 struct TALER_TESTING_Command 296 TALER_TESTING_cmd_refund (const char *label, 297 unsigned int expected_response_code, 298 const char *refund_amount, 299 const char *coin_reference) 300 { 301 struct RefundState *rs; 302 303 rs = GNUNET_new (struct RefundState); 304 rs->expected_response_code = expected_response_code; 305 rs->refund_amount = refund_amount; 306 rs->coin_reference = coin_reference; 307 { 308 struct TALER_TESTING_Command cmd = { 309 .cls = rs, 310 .label = label, 311 .run = &refund_run, 312 .cleanup = &refund_cleanup, 313 .traits = &refund_traits 314 }; 315 316 return cmd; 317 } 318 } 319 320 321 struct TALER_TESTING_Command 322 TALER_TESTING_cmd_refund_with_id ( 323 const char *label, 324 unsigned int expected_response_code, 325 const char *refund_amount, 326 const char *coin_reference, 327 uint64_t refund_transaction_id) 328 { 329 struct RefundState *rs; 330 331 rs = GNUNET_new (struct RefundState); 332 rs->expected_response_code = expected_response_code; 333 rs->refund_amount = refund_amount; 334 rs->coin_reference = coin_reference; 335 rs->refund_transaction_id = refund_transaction_id; 336 { 337 struct TALER_TESTING_Command cmd = { 338 .cls = rs, 339 .label = label, 340 .run = &refund_run, 341 .cleanup = &refund_cleanup, 342 .traits = &refund_traits 343 }; 344 345 return cmd; 346 } 347 }