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