Line data Source code
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 2 : refund_cb (
86 : struct WalletRefundState *wrs,
87 : const struct TALER_MERCHANT_PostOrdersRefundResponse *wrr)
88 : {
89 :
90 2 : wrs->orh = NULL;
91 2 : if (wrs->http_code != wrr->hr.http_status)
92 : {
93 0 : 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 0 : TALER_TESTING_FAIL (wrs->is);
99 : }
100 2 : switch (wrr->hr.http_status)
101 : {
102 2 : case MHD_HTTP_OK:
103 : {
104 : struct TALER_Amount refunded_total;
105 2 : if (wrr->details.ok.num_refunds != wrs->refunds_length)
106 : {
107 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
108 : "Number of refunds found (%u) does not match expected (%u)\n",
109 : wrr->details.ok.num_refunds,
110 : wrs->refunds_length);
111 0 : TALER_TESTING_interpreter_fail (wrs->is);
112 0 : return;
113 : }
114 2 : if (wrr->details.ok.num_refunds > 0)
115 2 : GNUNET_assert (GNUNET_OK ==
116 : TALER_amount_set_zero (
117 : wrr->details.ok.refunds[0].refund_amount.currency,
118 : &refunded_total));
119 6 : for (unsigned int i = 0; i < wrr->details.ok.num_refunds; ++i)
120 : {
121 4 : const struct TALER_MERCHANT_PostOrdersRefundDetail *refund
122 4 : = &wrr->details.ok.refunds[wrr->details.ok.num_refunds - 1 - i];
123 : const struct TALER_TESTING_Command *refund_cmd;
124 : const struct TALER_Amount *expected_amount;
125 :
126 4 : refund_cmd = TALER_TESTING_interpreter_lookup_command (
127 : wrs->is,
128 4 : wrs->refunds[i]);
129 :
130 4 : if (GNUNET_OK !=
131 4 : TALER_TESTING_get_trait_amount (refund_cmd,
132 : &expected_amount))
133 : {
134 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
135 : "Could not fetch refund amount\n");
136 0 : TALER_TESTING_interpreter_fail (wrs->is);
137 0 : return;
138 : }
139 : /* The most recent refunds are returned first */
140 4 : GNUNET_assert (0 <= TALER_amount_add (&refunded_total,
141 : &refunded_total,
142 : &refund->refund_amount));
143 4 : if ( (GNUNET_OK !=
144 4 : TALER_amount_cmp_currency (expected_amount,
145 4 : &refunded_total)) ||
146 4 : (0 != TALER_amount_cmp (expected_amount,
147 : &refunded_total)) )
148 : {
149 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150 : "Refund amounts do not match\n");
151 0 : TALER_TESTING_interpreter_fail (wrs->is);
152 0 : return;
153 : }
154 : }
155 : }
156 2 : break;
157 0 : default:
158 0 : break;
159 : }
160 2 : TALER_TESTING_interpreter_next (wrs->is);
161 : }
162 :
163 :
164 : /**
165 : * Run the "refund increase" CMD.
166 : *
167 : * @param cls closure.
168 : * @param cmd command currently being run.
169 : * @param is the interpreter state.
170 : */
171 : static void
172 2 : obtain_refunds_run (void *cls,
173 : const struct TALER_TESTING_Command *cmd,
174 : struct TALER_TESTING_Interpreter *is)
175 : {
176 2 : struct WalletRefundState *wrs = cls;
177 : const struct TALER_TESTING_Command *proposal_cmd =
178 2 : TALER_TESTING_interpreter_lookup_command (is,
179 : wrs->proposal_reference);
180 : const struct TALER_PrivateContractHashP *h_contract_terms;
181 : const char *order_id;
182 :
183 2 : if (NULL == proposal_cmd)
184 0 : TALER_TESTING_FAIL (is);
185 2 : if (GNUNET_OK !=
186 2 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
187 : &h_contract_terms))
188 0 : TALER_TESTING_FAIL (is);
189 :
190 : {
191 : const json_t *contract_terms;
192 : const char *error_name;
193 : unsigned int error_line;
194 :
195 2 : if (GNUNET_OK !=
196 2 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
197 : &contract_terms))
198 0 : TALER_TESTING_FAIL (is);
199 : {
200 : /* Get information that needs to be put verbatim in the
201 : * deposit permission */
202 : struct GNUNET_JSON_Specification spec[] = {
203 2 : TALER_JSON_spec_slug ("order_id",
204 : &order_id),
205 2 : GNUNET_JSON_spec_end ()
206 : };
207 :
208 2 : if (GNUNET_OK !=
209 2 : GNUNET_JSON_parse (contract_terms,
210 : spec,
211 : &error_name,
212 : &error_line))
213 : {
214 : char *js;
215 :
216 0 : js = json_dumps (contract_terms,
217 : JSON_INDENT (1));
218 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
219 : "Parser failed on %s:%u for input `%s'\n",
220 : error_name,
221 : error_line,
222 : js);
223 0 : free (js);
224 0 : TALER_TESTING_FAIL (is);
225 : }
226 : }
227 : }
228 :
229 2 : wrs->is = is;
230 2 : wrs->orh = TALER_MERCHANT_post_orders_refund_create (
231 : TALER_TESTING_interpreter_get_context (is),
232 : wrs->merchant_url,
233 : order_id,
234 : h_contract_terms);
235 : {
236 : enum TALER_ErrorCode ec;
237 :
238 2 : ec = TALER_MERCHANT_post_orders_refund_start (
239 : wrs->orh,
240 : &refund_cb,
241 : wrs);
242 2 : GNUNET_assert (TALER_EC_NONE == ec);
243 : }
244 : }
245 :
246 :
247 : /**
248 : * Free the state of a "refund increase" CMD, and
249 : * possibly cancel a pending "refund increase" operation.
250 : *
251 : * @param cls closure
252 : * @param cmd command currently being freed.
253 : */
254 : static void
255 2 : obtain_refunds_cleanup (void *cls,
256 : const struct TALER_TESTING_Command *cmd)
257 : {
258 2 : struct WalletRefundState *wrs = cls;
259 :
260 2 : if (NULL != wrs->orh)
261 : {
262 0 : TALER_LOG_WARNING ("Refund operation did not complete\n");
263 0 : TALER_MERCHANT_post_orders_refund_cancel (wrs->orh);
264 : }
265 2 : GNUNET_array_grow (wrs->refunds,
266 : wrs->refunds_length,
267 : 0);
268 2 : GNUNET_free (wrs);
269 2 : }
270 :
271 :
272 : struct TALER_TESTING_Command
273 2 : TALER_TESTING_cmd_wallet_order_refund (const char *label,
274 : const char *merchant_url,
275 : const char *order_ref,
276 : unsigned int http_code,
277 : ...)
278 : {
279 : struct WalletRefundState *wrs;
280 :
281 2 : wrs = GNUNET_new (struct WalletRefundState);
282 2 : wrs->merchant_url = merchant_url;
283 2 : wrs->proposal_reference = order_ref;
284 2 : wrs->http_code = http_code;
285 2 : wrs->refunds_length = 0;
286 : {
287 : const char *clabel;
288 : va_list ap;
289 :
290 2 : va_start (ap, http_code);
291 6 : while (NULL != (clabel = va_arg (ap, const char *)))
292 : {
293 4 : GNUNET_array_append (wrs->refunds,
294 : wrs->refunds_length,
295 : clabel);
296 : }
297 2 : va_end (ap);
298 : }
299 : {
300 2 : struct TALER_TESTING_Command cmd = {
301 : .cls = wrs,
302 : .label = label,
303 : .run = &obtain_refunds_run,
304 : .cleanup = &obtain_refunds_cleanup
305 : };
306 :
307 2 : return cmd;
308 : }
309 : }
|