testing_api_cmd_wallet_post_orders_refund.c (8207B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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_wallet_post_orders_refund.c 21 * @brief command to test refunds. 22 * @author Marcello Stanisci 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include <taler/taler_exchange_service.h> 27 #include <taler/taler_testing_lib.h> 28 #include "taler_merchant_service.h" 29 #include "taler_merchant_testing_lib.h" 30 31 32 /** 33 * State for an "obtain refunds" CMD. 34 */ 35 struct WalletRefundState 36 { 37 /** 38 * Operation handle for a (public) POST /orders/$ID/refund request. 39 */ 40 struct TALER_MERCHANT_WalletOrderRefundHandle *orh; 41 42 /** 43 * Base URL of the merchant serving the request. 44 */ 45 const char *merchant_url; 46 47 /** 48 * Interpreter state. 49 */ 50 struct TALER_TESTING_Interpreter *is; 51 52 /** 53 * Expected HTTP response code. 54 */ 55 unsigned int http_code; 56 57 /** 58 * Label of the command that created the order we want to obtain refunds for. 59 */ 60 const char *proposal_reference; 61 62 /** 63 * A list of refunds associated with this order. 64 */ 65 const char **refunds; 66 67 /** 68 * The length of @e refunds. 69 */ 70 unsigned int refunds_length; 71 }; 72 73 74 /** 75 * Process POST /refund (increase) response; just checking 76 * if the HTTP response code is the one expected. 77 * 78 * @param cls closure 79 * @param wrr response 80 */ 81 static void 82 refund_cb ( 83 void *cls, 84 const struct TALER_MERCHANT_WalletRefundResponse *wrr) 85 { 86 struct WalletRefundState *wrs = cls; 87 88 wrs->orh = NULL; 89 if (wrs->http_code != wrr->hr.http_status) 90 { 91 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 92 "Expected status %u, got %u(%d) for refund increase\n", 93 wrs->http_code, 94 wrr->hr.http_status, 95 (int) wrr->hr.ec); 96 TALER_TESTING_FAIL (wrs->is); 97 } 98 switch (wrr->hr.http_status) 99 { 100 case MHD_HTTP_OK: 101 { 102 struct TALER_Amount refunded_total; 103 if (wrr->details.ok.refunds_length > 0) 104 GNUNET_assert (GNUNET_OK == 105 TALER_amount_set_zero ( 106 wrr->details.ok.refunds[0].refund_amount.currency, 107 &refunded_total)); 108 for (unsigned int i = 0; i < wrr->details.ok.refunds_length; ++i) 109 { 110 const struct TALER_MERCHANT_RefundDetail *refund 111 = &wrr->details.ok.refunds[wrr->details.ok.refunds_length - 1 - i]; 112 const struct TALER_TESTING_Command *refund_cmd; 113 const struct TALER_Amount *expected_amount; 114 115 refund_cmd = TALER_TESTING_interpreter_lookup_command ( 116 wrs->is, 117 wrs->refunds[i]); 118 119 if (GNUNET_OK != 120 TALER_TESTING_get_trait_amount (refund_cmd, 121 &expected_amount)) 122 { 123 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 124 "Could not fetch refund amount\n"); 125 TALER_TESTING_interpreter_fail (wrs->is); 126 return; 127 } 128 /* The most recent refunds are returned first */ 129 GNUNET_assert (0 <= TALER_amount_add (&refunded_total, 130 &refunded_total, 131 &refund->refund_amount)); 132 if ( (GNUNET_OK != 133 TALER_amount_cmp_currency (expected_amount, 134 &refunded_total)) || 135 (0 != TALER_amount_cmp (expected_amount, 136 &refunded_total)) ) 137 { 138 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 139 "Refund amounts do not match\n"); 140 TALER_TESTING_interpreter_fail (wrs->is); 141 return; 142 } 143 } 144 } 145 break; 146 default: 147 break; 148 } 149 TALER_TESTING_interpreter_next (wrs->is); 150 } 151 152 153 /** 154 * Run the "refund increase" CMD. 155 * 156 * @param cls closure. 157 * @param cmd command currently being run. 158 * @param is the interpreter state. 159 */ 160 static void 161 obtain_refunds_run (void *cls, 162 const struct TALER_TESTING_Command *cmd, 163 struct TALER_TESTING_Interpreter *is) 164 { 165 struct WalletRefundState *wrs = cls; 166 const struct TALER_TESTING_Command *proposal_cmd = 167 TALER_TESTING_interpreter_lookup_command (is, 168 wrs->proposal_reference); 169 const struct TALER_PrivateContractHashP *h_contract_terms; 170 const char *order_id; 171 172 if (NULL == proposal_cmd) 173 TALER_TESTING_FAIL (is); 174 if (GNUNET_OK != 175 TALER_TESTING_get_trait_h_contract_terms (proposal_cmd, 176 &h_contract_terms)) 177 TALER_TESTING_FAIL (is); 178 179 { 180 const json_t *contract_terms; 181 const char *error_name; 182 unsigned int error_line; 183 184 if (GNUNET_OK != 185 TALER_TESTING_get_trait_contract_terms (proposal_cmd, 186 &contract_terms)) 187 TALER_TESTING_FAIL (is); 188 { 189 /* Get information that needs to be put verbatim in the 190 * deposit permission */ 191 struct GNUNET_JSON_Specification spec[] = { 192 GNUNET_JSON_spec_string ("order_id", 193 &order_id), 194 GNUNET_JSON_spec_end () 195 }; 196 197 if (GNUNET_OK != 198 GNUNET_JSON_parse (contract_terms, 199 spec, 200 &error_name, 201 &error_line)) 202 { 203 char *js; 204 205 js = json_dumps (contract_terms, 206 JSON_INDENT (1)); 207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 208 "Parser failed on %s:%u for input `%s'\n", 209 error_name, 210 error_line, 211 js); 212 free (js); 213 TALER_TESTING_FAIL (is); 214 } 215 } 216 } 217 218 wrs->is = is; 219 wrs->orh = TALER_MERCHANT_wallet_post_order_refund ( 220 TALER_TESTING_interpreter_get_context (is), 221 wrs->merchant_url, 222 order_id, 223 h_contract_terms, 224 &refund_cb, 225 wrs); 226 if (NULL == wrs->orh) 227 TALER_TESTING_FAIL (is); 228 } 229 230 231 /** 232 * Free the state of a "refund increase" CMD, and 233 * possibly cancel a pending "refund increase" operation. 234 * 235 * @param cls closure 236 * @param cmd command currently being freed. 237 */ 238 static void 239 obtain_refunds_cleanup (void *cls, 240 const struct TALER_TESTING_Command *cmd) 241 { 242 struct WalletRefundState *wrs = cls; 243 244 if (NULL != wrs->orh) 245 { 246 TALER_LOG_WARNING ("Refund operation did not complete\n"); 247 TALER_MERCHANT_wallet_post_order_refund_cancel (wrs->orh); 248 } 249 GNUNET_array_grow (wrs->refunds, 250 wrs->refunds_length, 251 0); 252 GNUNET_free (wrs); 253 } 254 255 256 struct TALER_TESTING_Command 257 TALER_TESTING_cmd_wallet_order_refund (const char *label, 258 const char *merchant_url, 259 const char *order_ref, 260 unsigned int http_code, 261 ...) 262 { 263 struct WalletRefundState *wrs; 264 265 wrs = GNUNET_new (struct WalletRefundState); 266 wrs->merchant_url = merchant_url; 267 wrs->proposal_reference = order_ref; 268 wrs->http_code = http_code; 269 wrs->refunds_length = 0; 270 { 271 const char *clabel; 272 va_list ap; 273 274 va_start (ap, http_code); 275 while (NULL != (clabel = va_arg (ap, const char *))) 276 { 277 GNUNET_array_append (wrs->refunds, 278 wrs->refunds_length, 279 clabel); 280 } 281 va_end (ap); 282 } 283 { 284 struct TALER_TESTING_Command cmd = { 285 .cls = wrs, 286 .label = label, 287 .run = &obtain_refunds_run, 288 .cleanup = &obtain_refunds_cleanup 289 }; 290 291 return cmd; 292 } 293 }