Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2020, 2023, 2024 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_post_transfers.c
21 : * @brief command to test POST /transfers
22 : * @author Christian Grothoff
23 : */
24 : #include "platform.h"
25 : #include <taler/taler_exchange_service.h>
26 : #include <taler/taler_testing_lib.h>
27 : #include "taler_merchant_service.h"
28 : #include "taler_merchant_testing_lib.h"
29 :
30 :
31 : /**
32 : * State of a "POST /transfers" CMD.
33 : */
34 : struct PostTransfersState
35 : {
36 :
37 : /**
38 : * Handle for a "POST /transfers" request.
39 : */
40 : struct TALER_MERCHANT_PostTransfersHandle *pth;
41 :
42 : /**
43 : * Handle for a "GET" bank account history request.
44 : */
45 : struct TALER_BANK_DebitHistoryHandle *dhh;
46 :
47 : /**
48 : * The interpreter state.
49 : */
50 : struct TALER_TESTING_Interpreter *is;
51 :
52 : /**
53 : * Base URL of the merchant serving the request.
54 : */
55 : const char *merchant_url;
56 :
57 : /**
58 : * URL of the bank to run history on.
59 : */
60 : char *exchange_url;
61 :
62 : /**
63 : * Credit account of the merchant.
64 : */
65 : struct TALER_FullPayto credit_account;
66 :
67 : /**
68 : * Payto URI to filter on.
69 : */
70 : struct TALER_FullPayto payto_uri;
71 :
72 : /**
73 : * Set to the hash of the @e payto_uri.
74 : */
75 : struct TALER_FullPaytoHashP h_payto;
76 :
77 : /**
78 : * Set to the hash of the normalized @e payto_uri.
79 : */
80 : struct TALER_NormalizedPaytoHashP h_normalized_payto;
81 :
82 : /**
83 : * Authentication details to authenticate to the bank.
84 : */
85 : struct TALER_BANK_AuthenticationData auth;
86 :
87 : /**
88 : * Set once we discovered the WTID.
89 : */
90 : struct TALER_WireTransferIdentifierRawP wtid;
91 :
92 : /**
93 : * the credit amount to look for at @e bank_url.
94 : */
95 : struct TALER_Amount credit_amount;
96 :
97 : /**
98 : * Expected HTTP response code.
99 : */
100 : unsigned int http_status;
101 :
102 : /**
103 : * Array of deposit command labels we expect to see aggregated.
104 : */
105 : const char **deposits;
106 :
107 : /**
108 : * Serial number of the wire transfer in the merchant backend,
109 : * set by #TALER_TESTING_cmd_merchant_get_transfers(). 0 if unknown.
110 : */
111 : uint64_t serial;
112 :
113 : /**
114 : * Length of @e deposits.
115 : */
116 : unsigned int deposits_length;
117 :
118 : };
119 :
120 :
121 : /**
122 : * Callback for a POST /transfers operation.
123 : *
124 : * @param cls closure for this function
125 : * @param ptr response details
126 : */
127 : static void
128 6 : transfers_cb (void *cls,
129 : const struct TALER_MERCHANT_PostTransfersResponse *ptr)
130 : {
131 6 : struct PostTransfersState *pts = cls;
132 :
133 6 : pts->pth = NULL;
134 6 : if (pts->http_status != ptr->hr.http_status)
135 : {
136 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
137 : "Unexpected response code %u (%d) to command %s\n",
138 : ptr->hr.http_status,
139 : (int) ptr->hr.ec,
140 : TALER_TESTING_interpreter_get_current_label (pts->is));
141 0 : GNUNET_break (0);
142 0 : TALER_TESTING_interpreter_fail (pts->is);
143 0 : return;
144 : }
145 6 : switch (ptr->hr.http_status)
146 : {
147 6 : case MHD_HTTP_NO_CONTENT:
148 6 : break;
149 0 : case MHD_HTTP_UNAUTHORIZED:
150 0 : break;
151 0 : case MHD_HTTP_NOT_FOUND:
152 0 : break;
153 0 : case MHD_HTTP_GATEWAY_TIMEOUT:
154 0 : break;
155 0 : default:
156 0 : GNUNET_break (0);
157 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
158 : "Unhandled HTTP status %u for POST /transfers.\n",
159 : ptr->hr.http_status);
160 : }
161 6 : TALER_TESTING_interpreter_next (pts->is);
162 : }
163 :
164 :
165 : /**
166 : * Offers information from the POST /transfers CMD state to other
167 : * commands.
168 : *
169 : * @param cls closure
170 : * @param[out] ret result (could be anything)
171 : * @param trait name of the trait
172 : * @param index index number of the object to extract.
173 : * @return #GNUNET_OK on success
174 : */
175 : static enum GNUNET_GenericReturnValue
176 48 : post_transfers_traits (void *cls,
177 : const void **ret,
178 : const char *trait,
179 : unsigned int index)
180 : {
181 48 : struct PostTransfersState *pts = cls;
182 : struct TALER_TESTING_Trait traits[] = {
183 48 : TALER_TESTING_make_trait_wtid (&pts->wtid),
184 48 : TALER_TESTING_make_trait_credit_payto_uri (&pts->credit_account),
185 48 : TALER_TESTING_make_trait_h_full_payto (&pts->h_payto),
186 48 : TALER_TESTING_make_trait_h_normalized_payto (&pts->h_normalized_payto),
187 48 : TALER_TESTING_make_trait_amount (&pts->credit_amount),
188 48 : TALER_TESTING_make_trait_exchange_url (pts->exchange_url),
189 48 : TALER_TESTING_make_trait_bank_row (&pts->serial),
190 48 : TALER_TESTING_trait_end (),
191 : };
192 :
193 48 : return TALER_TESTING_get_trait (traits,
194 : ret,
195 : trait,
196 : index);
197 : }
198 :
199 :
200 : /**
201 : * Run the "POST /transfers" CMD. First, get the bank history to find
202 : * the wtid.
203 : *
204 : * @param cls closure.
205 : * @param cmd command being run now.
206 : * @param is interpreter state.
207 : */
208 : static void
209 2 : post_transfers_run2 (void *cls,
210 : const struct TALER_TESTING_Command *cmd,
211 : struct TALER_TESTING_Interpreter *is)
212 : {
213 2 : struct PostTransfersState *pts = cls;
214 :
215 2 : pts->is = is;
216 2 : pts->pth = TALER_MERCHANT_transfers_post (
217 : TALER_TESTING_interpreter_get_context (pts->is),
218 : pts->merchant_url,
219 2 : &pts->credit_amount,
220 2 : &pts->wtid,
221 : pts->credit_account,
222 2 : pts->exchange_url,
223 : &transfers_cb,
224 : pts);
225 2 : GNUNET_assert (NULL != pts->pth);
226 2 : }
227 :
228 :
229 : /**
230 : * Callbacks of this type are used to serve the result of asking
231 : * the bank for the debit transaction history.
232 : *
233 : * @param cls closure with a `struct PostTransfersState *`
234 : * @param reply details from the HTTP response code
235 : */
236 : static void
237 4 : debit_cb (
238 : void *cls,
239 : const struct TALER_BANK_DebitHistoryResponse *reply)
240 : {
241 4 : struct PostTransfersState *pts = cls;
242 :
243 4 : pts->dhh = NULL;
244 4 : switch (reply->http_status)
245 : {
246 4 : case MHD_HTTP_OK:
247 : /* handled below */
248 4 : break;
249 0 : case MHD_HTTP_NO_CONTENT:
250 0 : GNUNET_break (0);
251 0 : TALER_TESTING_interpreter_fail (pts->is);
252 0 : return;
253 0 : default:
254 0 : GNUNET_break (0);
255 0 : TALER_TESTING_interpreter_fail (pts->is);
256 0 : return;
257 : }
258 4 : for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
259 : {
260 4 : const struct TALER_BANK_DebitDetails *details
261 4 : = &reply->details.ok.details[i];
262 :
263 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
264 : "Bank reports transfer of %s to %s\n",
265 : TALER_amount2s (&details->amount),
266 : details->credit_account_uri.full_payto);
267 4 : if (0 != TALER_amount_cmp (&pts->credit_amount,
268 : &details->amount))
269 0 : continue;
270 4 : pts->wtid = details->wtid;
271 : pts->credit_account.full_payto
272 4 : = GNUNET_strdup (details->credit_account_uri.full_payto);
273 4 : pts->exchange_url = GNUNET_strdup (details->exchange_base_url);
274 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
275 : "Bank transfer found, checking with merchant backend at %s about %s from %s to %s with %s\n",
276 : pts->merchant_url,
277 : TALER_amount2s (&pts->credit_amount),
278 : pts->payto_uri.full_payto,
279 : pts->exchange_url,
280 : TALER_B2S (&pts->wtid));
281 4 : pts->pth = TALER_MERCHANT_transfers_post (
282 : TALER_TESTING_interpreter_get_context (pts->is),
283 : pts->merchant_url,
284 4 : &pts->credit_amount,
285 4 : &pts->wtid,
286 : pts->credit_account,
287 4 : pts->exchange_url,
288 : &transfers_cb,
289 : pts);
290 4 : GNUNET_assert (NULL != pts->pth);
291 4 : break;
292 : }
293 4 : if (NULL == pts->pth)
294 : {
295 0 : GNUNET_break (0);
296 0 : TALER_TESTING_interpreter_fail (pts->is);
297 0 : return;
298 : }
299 : }
300 :
301 :
302 : /**
303 : * Run the "POST /transfers" CMD. First, get the bank history to find
304 : * the wtid.
305 : *
306 : * @param cls closure.
307 : * @param cmd command being run now.
308 : * @param is interpreter state.
309 : */
310 : static void
311 4 : post_transfers_run (void *cls,
312 : const struct TALER_TESTING_Command *cmd,
313 : struct TALER_TESTING_Interpreter *is)
314 : {
315 4 : struct PostTransfersState *pts = cls;
316 :
317 4 : pts->is = is;
318 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
319 : "Looking for transfer of %s from %s at bank\n",
320 : TALER_amount2s (&pts->credit_amount),
321 : pts->payto_uri.full_payto);
322 4 : pts->dhh = TALER_BANK_debit_history (TALER_TESTING_interpreter_get_context (
323 : is),
324 4 : &pts->auth,
325 : UINT64_MAX,
326 : -INT64_MAX,
327 4 : GNUNET_TIME_UNIT_ZERO,
328 : &debit_cb,
329 : pts);
330 4 : GNUNET_assert (NULL != pts->dhh);
331 4 : }
332 :
333 :
334 : /**
335 : * Free the state of a "POST product" CMD, and possibly
336 : * cancel a pending operation thereof.
337 : *
338 : * @param cls closure.
339 : * @param cmd command being run.
340 : */
341 : static void
342 6 : post_transfers_cleanup (void *cls,
343 : const struct TALER_TESTING_Command *cmd)
344 : {
345 6 : struct PostTransfersState *pts = cls;
346 :
347 6 : if (NULL != pts->pth)
348 : {
349 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
350 : "POST /transfers operation did not complete\n");
351 0 : TALER_MERCHANT_transfers_post_cancel (pts->pth);
352 : }
353 6 : if (NULL != pts->dhh)
354 : {
355 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
356 : "GET debit history operation did not complete\n");
357 0 : TALER_BANK_debit_history_cancel (pts->dhh);
358 : }
359 6 : GNUNET_array_grow (pts->deposits,
360 : pts->deposits_length,
361 : 0);
362 6 : GNUNET_free (pts->exchange_url);
363 6 : GNUNET_free (pts->credit_account.full_payto);
364 6 : GNUNET_free (pts);
365 6 : }
366 :
367 :
368 : struct TALER_TESTING_Command
369 4 : TALER_TESTING_cmd_merchant_post_transfer (
370 : const char *label,
371 : const struct TALER_BANK_AuthenticationData *auth,
372 : struct TALER_FullPayto payto_uri,
373 : const char *merchant_url,
374 : const char *credit_amount,
375 : unsigned int http_code,
376 : ...)
377 : {
378 : struct PostTransfersState *pts;
379 :
380 4 : pts = GNUNET_new (struct PostTransfersState);
381 4 : pts->merchant_url = merchant_url;
382 4 : pts->auth = *auth;
383 4 : pts->payto_uri = payto_uri;
384 4 : TALER_full_payto_hash (payto_uri,
385 : &pts->h_payto);
386 4 : TALER_full_payto_normalize_and_hash (payto_uri,
387 : &pts->h_normalized_payto);
388 4 : GNUNET_assert (GNUNET_OK ==
389 : TALER_string_to_amount (credit_amount,
390 : &pts->credit_amount));
391 4 : pts->http_status = http_code;
392 : {
393 : const char *clabel;
394 : va_list ap;
395 :
396 4 : va_start (ap, http_code);
397 8 : while (NULL != (clabel = va_arg (ap, const char *)))
398 : {
399 4 : GNUNET_array_append (pts->deposits,
400 : pts->deposits_length,
401 : clabel);
402 : }
403 4 : va_end (ap);
404 : }
405 : {
406 4 : struct TALER_TESTING_Command cmd = {
407 : .cls = pts,
408 : .label = label,
409 : .run = &post_transfers_run,
410 : .cleanup = &post_transfers_cleanup,
411 : .traits = &post_transfers_traits
412 : };
413 :
414 4 : return cmd;
415 : }
416 : }
417 :
418 :
419 : struct TALER_TESTING_Command
420 2 : TALER_TESTING_cmd_merchant_post_transfer2 (
421 : const char *label,
422 : const char *merchant_url,
423 : struct TALER_FullPayto payto_uri,
424 : const char *credit_amount,
425 : const char *wtid,
426 : const char *exchange_url,
427 : unsigned int http_code)
428 : {
429 : struct PostTransfersState *pts;
430 :
431 2 : pts = GNUNET_new (struct PostTransfersState);
432 2 : pts->merchant_url = merchant_url;
433 : pts->credit_account.full_payto
434 2 : = GNUNET_strdup (payto_uri.full_payto);
435 2 : pts->exchange_url = GNUNET_strdup (exchange_url);
436 2 : GNUNET_assert (GNUNET_OK ==
437 : TALER_string_to_amount (credit_amount,
438 : &pts->credit_amount));
439 2 : if (NULL == wtid)
440 : {
441 2 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
442 2 : &pts->wtid,
443 : sizeof (pts->wtid));
444 : }
445 : else
446 : {
447 0 : GNUNET_assert (GNUNET_OK ==
448 : GNUNET_STRINGS_string_to_data (wtid,
449 : strlen (wtid),
450 : &pts->wtid,
451 : sizeof (pts->wtid)));
452 : }
453 2 : pts->http_status = http_code;
454 : {
455 2 : struct TALER_TESTING_Command cmd = {
456 : .cls = pts,
457 : .label = label,
458 : .run = &post_transfers_run2,
459 : .cleanup = &post_transfers_cleanup,
460 : .traits = &post_transfers_traits
461 : };
462 :
463 2 : return cmd;
464 : }
465 : }
466 :
467 :
468 : void
469 7 : TALER_TESTING_cmd_merchant_post_transfer_set_serial (
470 : struct TALER_TESTING_Command *cmd,
471 : uint64_t serial)
472 : {
473 7 : struct PostTransfersState *pts = cmd->cls;
474 :
475 7 : GNUNET_assert (cmd->run = &post_transfers_run);
476 7 : pts->serial = serial;
477 7 : }
478 :
479 :
480 : /* end of testing_api_cmd_post_transfers.c */
|