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