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