Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-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/testing_api_cmd_refund.c
21 : * @brief Implement the /refund test command, plus other
22 : * corollary commands (?).
23 : * @author Marcello Stanisci
24 : */
25 : #include "taler/taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : struct RefundState;
28 : #define TALER_EXCHANGE_POST_COINS_REFUND_RESULT_CLOSURE struct RefundState
29 : #include "taler/exchange/post-coins-COIN_PUB-refund.h"
30 : #include "taler/taler_testing_lib.h"
31 :
32 :
33 : /**
34 : * State for a "refund" CMD.
35 : */
36 : struct RefundState
37 : {
38 : /**
39 : * Expected HTTP response code.
40 : */
41 : unsigned int expected_response_code;
42 :
43 : /**
44 : * Amount to be refunded.
45 : */
46 : const char *refund_amount;
47 :
48 : /**
49 : * Reference to any command that can provide a coin to refund.
50 : */
51 : const char *coin_reference;
52 :
53 : /**
54 : * Refund transaction identifier.
55 : */
56 : uint64_t refund_transaction_id;
57 :
58 : /**
59 : * Entry in the coin's history generated by this operation.
60 : */
61 : struct TALER_EXCHANGE_CoinHistoryEntry che;
62 :
63 : /**
64 : * Public key of the refunded coin.
65 : */
66 : struct TALER_CoinSpendPublicKeyP coin;
67 :
68 : /**
69 : * Handle to the refund operation.
70 : */
71 : struct TALER_EXCHANGE_PostCoinsRefundHandle *rh;
72 :
73 : /**
74 : * Interpreter state.
75 : */
76 : struct TALER_TESTING_Interpreter *is;
77 : };
78 :
79 :
80 : /**
81 : * Check the result for the refund request, just check if the
82 : * response code is acceptable.
83 : *
84 : * @param rs closure
85 : * @param rr response details
86 : */
87 : static void
88 14 : refund_cb (struct RefundState *rs,
89 : const struct TALER_EXCHANGE_PostCoinsRefundResponse *rr)
90 : {
91 14 : const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
92 :
93 14 : rs->rh = NULL;
94 14 : if (rs->expected_response_code != hr->http_status)
95 : {
96 0 : TALER_TESTING_unexpected_status (rs->is,
97 : hr->http_status,
98 : rs->expected_response_code);
99 0 : return;
100 : }
101 14 : if (MHD_HTTP_OK == hr->http_status)
102 : {
103 : struct TALER_Amount refund_amount;
104 :
105 10 : if (GNUNET_OK !=
106 10 : TALER_string_to_amount (rs->refund_amount,
107 : &refund_amount))
108 : {
109 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110 : "Failed to parse amount `%s'\n",
111 : rs->refund_amount);
112 0 : TALER_TESTING_interpreter_fail (rs->is);
113 0 : return;
114 : }
115 10 : if (0 >
116 10 : TALER_amount_subtract (&rs->che.amount,
117 : &refund_amount,
118 10 : &rs->che.details.refund.refund_fee))
119 : {
120 0 : GNUNET_break (0);
121 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
122 : "Failed to subtract %s from %s\n",
123 : TALER_amount2s (&rs->che.details.refund.refund_fee),
124 : rs->refund_amount);
125 0 : TALER_TESTING_interpreter_fail (rs->is);
126 0 : return;
127 : }
128 : }
129 14 : TALER_TESTING_interpreter_next (rs->is);
130 : }
131 :
132 :
133 : /**
134 : * Run the command.
135 : *
136 : * @param cls closure.
137 : * @param cmd the command to execute.
138 : * @param is the interpreter state.
139 : */
140 : static void
141 14 : refund_run (void *cls,
142 : const struct TALER_TESTING_Command *cmd,
143 : struct TALER_TESTING_Interpreter *is)
144 : {
145 14 : struct RefundState *rs = cls;
146 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
147 : const json_t *contract_terms;
148 : struct TALER_PrivateContractHashP h_contract_terms;
149 : struct TALER_Amount refund_amount;
150 : const struct TALER_MerchantPrivateKeyP *merchant_priv;
151 : const struct TALER_TESTING_Command *coin_cmd;
152 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
153 :
154 14 : rs->is = is;
155 14 : if (GNUNET_OK !=
156 14 : TALER_string_to_amount (rs->refund_amount,
157 : &refund_amount))
158 : {
159 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
160 : "Failed to parse amount `%s' at %s\n",
161 : rs->refund_amount,
162 : cmd->label);
163 0 : TALER_TESTING_interpreter_fail (is);
164 0 : return;
165 : }
166 14 : coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
167 : rs->coin_reference);
168 14 : if (NULL == coin_cmd)
169 : {
170 0 : GNUNET_break (0);
171 0 : TALER_TESTING_interpreter_fail (is);
172 0 : return;
173 : }
174 14 : if (GNUNET_OK !=
175 14 : TALER_TESTING_get_trait_contract_terms (coin_cmd,
176 : &contract_terms))
177 : {
178 0 : GNUNET_break (0);
179 0 : TALER_TESTING_interpreter_fail (is);
180 0 : return;
181 : }
182 14 : GNUNET_assert (GNUNET_OK ==
183 : TALER_JSON_contract_hash (contract_terms,
184 : &h_contract_terms));
185 :
186 : /* Hunting for a coin .. */
187 14 : if ( (GNUNET_OK !=
188 14 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
189 : 0,
190 14 : &coin_priv)) ||
191 : (GNUNET_OK !=
192 14 : TALER_TESTING_get_trait_denom_pub (coin_cmd,
193 : 0,
194 : &denom_pub)) )
195 :
196 : {
197 0 : GNUNET_break (0);
198 0 : TALER_TESTING_interpreter_fail (is);
199 0 : return;
200 : }
201 :
202 14 : GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
203 : &rs->coin.eddsa_pub);
204 14 : if (GNUNET_OK !=
205 14 : TALER_TESTING_get_trait_merchant_priv (coin_cmd,
206 : &merchant_priv))
207 : {
208 0 : GNUNET_break (0);
209 0 : TALER_TESTING_interpreter_fail (is);
210 0 : return;
211 : }
212 14 : rs->che.type = TALER_EXCHANGE_CTT_REFUND;
213 14 : rs->che.details.refund.h_contract_terms = h_contract_terms;
214 14 : GNUNET_CRYPTO_eddsa_key_get_public (
215 14 : &merchant_priv->eddsa_priv,
216 : &rs->che.details.refund.merchant_pub.eddsa_pub);
217 14 : rs->che.details.refund.refund_fee = denom_pub->fees.refund;
218 14 : rs->che.details.refund.sig_amount = refund_amount;
219 14 : rs->che.details.refund.rtransaction_id = rs->refund_transaction_id;
220 14 : TALER_merchant_refund_sign (&rs->coin,
221 : &h_contract_terms,
222 : rs->refund_transaction_id,
223 : &refund_amount,
224 : merchant_priv,
225 : &rs->che.details.refund.sig);
226 14 : rs->rh = TALER_EXCHANGE_post_coins_refund_create (
227 : TALER_TESTING_interpreter_get_context (is),
228 : TALER_TESTING_get_exchange_url (is),
229 : TALER_TESTING_get_keys (is),
230 : &refund_amount,
231 : &h_contract_terms,
232 14 : &rs->coin,
233 : rs->refund_transaction_id,
234 : merchant_priv);
235 14 : GNUNET_assert (NULL != rs->rh);
236 14 : GNUNET_assert (TALER_EC_NONE ==
237 : TALER_EXCHANGE_post_coins_refund_start (rs->rh,
238 : &refund_cb,
239 : rs));
240 : }
241 :
242 :
243 : /**
244 : * Offer internal data from a "refund" CMD, to other commands.
245 : *
246 : * @param cls closure.
247 : * @param[out] ret result.
248 : * @param trait name of the trait.
249 : * @param index index number of the object to offer.
250 : * @return #GNUNET_OK on success.
251 : */
252 : static enum GNUNET_GenericReturnValue
253 36 : refund_traits (void *cls,
254 : const void **ret,
255 : const char *trait,
256 : unsigned int index)
257 : {
258 36 : struct RefundState *rs = cls;
259 : struct TALER_TESTING_Trait traits[] = {
260 36 : TALER_TESTING_make_trait_coin_history (0,
261 36 : &rs->che),
262 36 : TALER_TESTING_make_trait_coin_pub (0,
263 36 : &rs->coin),
264 36 : TALER_TESTING_trait_end ()
265 : };
266 :
267 36 : return TALER_TESTING_get_trait (traits,
268 : ret,
269 : trait,
270 : index);
271 : }
272 :
273 :
274 : /**
275 : * Free the state from a "refund" CMD, and possibly cancel
276 : * a pending operation thereof.
277 : *
278 : * @param cls closure.
279 : * @param cmd the command which is being cleaned up.
280 : */
281 : static void
282 14 : refund_cleanup (void *cls,
283 : const struct TALER_TESTING_Command *cmd)
284 : {
285 14 : struct RefundState *rs = cls;
286 :
287 14 : if (NULL != rs->rh)
288 : {
289 0 : TALER_TESTING_command_incomplete (rs->is,
290 : cmd->label);
291 0 : TALER_EXCHANGE_post_coins_refund_cancel (rs->rh);
292 0 : rs->rh = NULL;
293 : }
294 14 : GNUNET_free (rs);
295 14 : }
296 :
297 :
298 : struct TALER_TESTING_Command
299 6 : TALER_TESTING_cmd_refund (const char *label,
300 : unsigned int expected_response_code,
301 : const char *refund_amount,
302 : const char *coin_reference)
303 : {
304 : struct RefundState *rs;
305 :
306 6 : rs = GNUNET_new (struct RefundState);
307 6 : rs->expected_response_code = expected_response_code;
308 6 : rs->refund_amount = refund_amount;
309 6 : rs->coin_reference = coin_reference;
310 : {
311 6 : struct TALER_TESTING_Command cmd = {
312 : .cls = rs,
313 : .label = label,
314 : .run = &refund_run,
315 : .cleanup = &refund_cleanup,
316 : .traits = &refund_traits
317 : };
318 :
319 6 : return cmd;
320 : }
321 : }
322 :
323 :
324 : struct TALER_TESTING_Command
325 8 : TALER_TESTING_cmd_refund_with_id (
326 : const char *label,
327 : unsigned int expected_response_code,
328 : const char *refund_amount,
329 : const char *coin_reference,
330 : uint64_t refund_transaction_id)
331 : {
332 : struct RefundState *rs;
333 :
334 8 : rs = GNUNET_new (struct RefundState);
335 8 : rs->expected_response_code = expected_response_code;
336 8 : rs->refund_amount = refund_amount;
337 8 : rs->coin_reference = coin_reference;
338 8 : rs->refund_transaction_id = refund_transaction_id;
339 : {
340 8 : struct TALER_TESTING_Command cmd = {
341 : .cls = rs,
342 : .label = label,
343 : .run = &refund_run,
344 : .cleanup = &refund_cleanup,
345 : .traits = &refund_traits
346 : };
347 :
348 8 : return cmd;
349 : }
350 : }
|