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 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 2 : refund_cb (
83 : void *cls,
84 : const struct TALER_MERCHANT_WalletRefundResponse *wrr)
85 : {
86 2 : struct WalletRefundState *wrs = cls;
87 :
88 2 : wrs->orh = NULL;
89 2 : if (wrs->http_code != wrr->hr.http_status)
90 : {
91 0 : 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 0 : TALER_TESTING_FAIL (wrs->is);
97 : }
98 2 : switch (wrr->hr.http_status)
99 : {
100 2 : case MHD_HTTP_OK:
101 : {
102 : struct TALER_Amount refunded_total;
103 2 : if (wrr->details.ok.refunds_length > 0)
104 2 : GNUNET_assert (GNUNET_OK ==
105 : TALER_amount_set_zero (
106 : wrr->details.ok.refunds[0].refund_amount.currency,
107 : &refunded_total));
108 6 : for (unsigned int i = 0; i < wrr->details.ok.refunds_length; ++i)
109 : {
110 4 : const struct TALER_MERCHANT_RefundDetail *refund
111 4 : = &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 4 : refund_cmd = TALER_TESTING_interpreter_lookup_command (
116 : wrs->is,
117 4 : wrs->refunds[i]);
118 :
119 4 : if (GNUNET_OK !=
120 4 : TALER_TESTING_get_trait_amount (refund_cmd,
121 : &expected_amount))
122 : {
123 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
124 : "Could not fetch refund amount\n");
125 0 : TALER_TESTING_interpreter_fail (wrs->is);
126 0 : return;
127 : }
128 : /* The most recent refunds are returned first */
129 4 : GNUNET_assert (0 <= TALER_amount_add (&refunded_total,
130 : &refunded_total,
131 : &refund->refund_amount));
132 4 : if ( (GNUNET_OK !=
133 4 : TALER_amount_cmp_currency (expected_amount,
134 4 : &refunded_total)) ||
135 4 : (0 != TALER_amount_cmp (expected_amount,
136 : &refunded_total)) )
137 : {
138 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
139 : "Refund amounts do not match\n");
140 0 : TALER_TESTING_interpreter_fail (wrs->is);
141 0 : return;
142 : }
143 : }
144 : }
145 2 : break;
146 0 : default:
147 0 : break;
148 : }
149 2 : 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 2 : obtain_refunds_run (void *cls,
162 : const struct TALER_TESTING_Command *cmd,
163 : struct TALER_TESTING_Interpreter *is)
164 : {
165 2 : struct WalletRefundState *wrs = cls;
166 : const struct TALER_TESTING_Command *proposal_cmd =
167 2 : 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 2 : if (NULL == proposal_cmd)
173 0 : TALER_TESTING_FAIL (is);
174 2 : if (GNUNET_OK !=
175 2 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
176 : &h_contract_terms))
177 0 : TALER_TESTING_FAIL (is);
178 :
179 : {
180 : const json_t *contract_terms;
181 : const char *error_name;
182 : unsigned int error_line;
183 :
184 2 : if (GNUNET_OK !=
185 2 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
186 : &contract_terms))
187 0 : 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 2 : GNUNET_JSON_spec_string ("order_id",
193 : &order_id),
194 2 : GNUNET_JSON_spec_end ()
195 : };
196 :
197 2 : if (GNUNET_OK !=
198 2 : GNUNET_JSON_parse (contract_terms,
199 : spec,
200 : &error_name,
201 : &error_line))
202 : {
203 : char *js;
204 :
205 0 : js = json_dumps (contract_terms,
206 : JSON_INDENT (1));
207 0 : 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 0 : free (js);
213 0 : TALER_TESTING_FAIL (is);
214 : }
215 : }
216 : }
217 :
218 2 : wrs->is = is;
219 2 : 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 2 : if (NULL == wrs->orh)
227 0 : 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 2 : obtain_refunds_cleanup (void *cls,
240 : const struct TALER_TESTING_Command *cmd)
241 : {
242 2 : struct WalletRefundState *wrs = cls;
243 :
244 2 : if (NULL != wrs->orh)
245 : {
246 0 : TALER_LOG_WARNING ("Refund operation did not complete\n");
247 0 : TALER_MERCHANT_wallet_post_order_refund_cancel (wrs->orh);
248 : }
249 2 : GNUNET_array_grow (wrs->refunds,
250 : wrs->refunds_length,
251 : 0);
252 2 : GNUNET_free (wrs);
253 2 : }
254 :
255 :
256 : struct TALER_TESTING_Command
257 2 : 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 2 : wrs = GNUNET_new (struct WalletRefundState);
266 2 : wrs->merchant_url = merchant_url;
267 2 : wrs->proposal_reference = order_ref;
268 2 : wrs->http_code = http_code;
269 2 : wrs->refunds_length = 0;
270 : {
271 : const char *clabel;
272 : va_list ap;
273 :
274 2 : va_start (ap, http_code);
275 6 : while (NULL != (clabel = va_arg (ap, const char *)))
276 : {
277 4 : GNUNET_array_append (wrs->refunds,
278 : wrs->refunds_length,
279 : clabel);
280 : }
281 2 : va_end (ap);
282 : }
283 : {
284 2 : struct TALER_TESTING_Command cmd = {
285 : .cls = wrs,
286 : .label = label,
287 : .run = &obtain_refunds_run,
288 : .cleanup = &obtain_refunds_cleanup
289 : };
290 :
291 2 : return cmd;
292 : }
293 : }
|