Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014--2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file plugin_merchantdb_postgres.c
18 : * @brief database helper functions for postgres used by the merchant
19 : * @author Sree Harsha Totakura <sreeharsha@totakura.in>
20 : * @author Christian Grothoff
21 : * @author Marcello Stanisci
22 : */
23 : #include "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_pq_lib.h>
26 : #include <taler/taler_util.h>
27 : #include <taler/taler_pq_lib.h>
28 : #include <taler/taler_json_lib.h>
29 : #include <taler/taler_mhd_lib.h>
30 : #include "taler_merchantdb_plugin.h"
31 :
32 : /**
33 : * How often do we re-try if we run into a DB serialization error?
34 : */
35 : #define MAX_RETRIES 3
36 :
37 :
38 : /**
39 : * Wrapper macro to add the currency from the plugin's state
40 : * when fetching amounts from the database.
41 : *
42 : * @param field name of the database field to fetch amount from
43 : * @param[out] amountp pointer to amount to set
44 : */
45 : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \
46 : TALER_PQ_result_spec_amount ( \
47 : field,pg->currency,amountp)
48 :
49 : /**
50 : * Wrapper macro to add the currency from the plugin's state
51 : * when fetching amounts from the database. NBO variant.
52 : *
53 : * @param field name of the database field to fetch amount from
54 : * @param[out] amountp pointer to amount to set
55 : */
56 : #define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, amountp) \
57 : TALER_PQ_result_spec_amount_nbo ( \
58 : field,pg->currency,amountp)
59 :
60 :
61 : /**
62 : * Wrapper macro to add the currency from the plugin's state
63 : * when fetching amounts from the database.
64 : *
65 : * @param field name of the database field to fetch amount from
66 : * @param[out] amountp pointer to amount to set
67 : */
68 : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \
69 : TALER_PQ_result_spec_amount ( \
70 : field,pg->currency,amountp)
71 :
72 :
73 : /**
74 : * Type of the "cls" argument given to each of the functions in
75 : * our API.
76 : */
77 : struct PostgresClosure
78 : {
79 :
80 : /**
81 : * Postgres connection handle.
82 : */
83 : struct GNUNET_PQ_Context *conn;
84 :
85 : /**
86 : * Which currency do we deal in?
87 : */
88 : char *currency;
89 :
90 : /**
91 : * Directory with SQL statements to run to create tables.
92 : */
93 : char *sql_dir;
94 :
95 : /**
96 : * Underlying configuration.
97 : */
98 : const struct GNUNET_CONFIGURATION_Handle *cfg;
99 :
100 : /**
101 : * Name of the currently active transaction, NULL if none is active.
102 : */
103 : const char *transaction_name;
104 :
105 : };
106 :
107 :
108 : /**
109 : * Drop all Taler tables. This should only be used by testcases.
110 : *
111 : * @param cls the `struct PostgresClosure` with the plugin-specific state
112 : * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
113 : */
114 : static enum GNUNET_GenericReturnValue
115 6 : postgres_drop_tables (void *cls)
116 : {
117 6 : struct PostgresClosure *pc = cls;
118 : struct GNUNET_PQ_Context *conn;
119 : enum GNUNET_GenericReturnValue ret;
120 :
121 6 : conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
122 : "merchantdb-postgres",
123 : NULL,
124 : NULL,
125 : NULL);
126 6 : if (NULL == conn)
127 6 : return GNUNET_SYSERR;
128 0 : ret = GNUNET_PQ_exec_sql (conn,
129 : "drop");
130 0 : GNUNET_PQ_disconnect (conn);
131 0 : return ret;
132 : }
133 :
134 :
135 : /**
136 : * Initialize tables.
137 : *
138 : * @param cls the `struct PostgresClosure` with the plugin-specific state
139 : * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
140 : */
141 : static enum GNUNET_GenericReturnValue
142 6 : postgres_create_tables (void *cls)
143 : {
144 6 : struct PostgresClosure *pc = cls;
145 : struct GNUNET_PQ_Context *conn;
146 6 : struct GNUNET_PQ_ExecuteStatement es[] = {
147 6 : GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"),
148 : GNUNET_PQ_EXECUTE_STATEMENT_END
149 : };
150 :
151 6 : conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
152 : "merchantdb-postgres",
153 : "merchant-",
154 : es,
155 : NULL);
156 6 : if (NULL == conn)
157 6 : return GNUNET_SYSERR;
158 0 : GNUNET_PQ_disconnect (conn);
159 0 : return GNUNET_OK;
160 : }
161 :
162 :
163 : /**
164 : * Register callback to be invoked on events of type @a es.
165 : *
166 : * @param cls database context to use
167 : * @param es specification of the event to listen for
168 : * @param timeout how long to wait for the event
169 : * @param cb function to call when the event happens, possibly
170 : * multiple times (until cancel is invoked)
171 : * @param cb_cls closure for @a cb
172 : * @return handle useful to cancel the listener
173 : */
174 : static struct GNUNET_DB_EventHandler *
175 0 : postgres_event_listen (void *cls,
176 : const struct GNUNET_DB_EventHeaderP *es,
177 : struct GNUNET_TIME_Relative timeout,
178 : GNUNET_DB_EventCallback cb,
179 : void *cb_cls)
180 : {
181 0 : struct PostgresClosure *pg = cls;
182 :
183 0 : return GNUNET_PQ_event_listen (pg->conn,
184 : es,
185 : timeout,
186 : cb,
187 : cb_cls);
188 : }
189 :
190 :
191 : /**
192 : * Stop notifications.
193 : *
194 : * @param eh handle to unregister.
195 : */
196 : static void
197 0 : postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
198 : {
199 0 : GNUNET_PQ_event_listen_cancel (eh);
200 0 : }
201 :
202 :
203 : /**
204 : * Notify all that listen on @a es of an event.
205 : *
206 : * @param cls database context to use
207 : * @param es specification of the event to generate
208 : * @param extra additional event data provided
209 : * @param extra_size number of bytes in @a extra
210 : */
211 : static void
212 0 : postgres_event_notify (void *cls,
213 : const struct GNUNET_DB_EventHeaderP *es,
214 : const void *extra,
215 : size_t extra_size)
216 : {
217 0 : struct PostgresClosure *pg = cls;
218 :
219 0 : return GNUNET_PQ_event_notify (pg->conn,
220 : es,
221 : extra,
222 : extra_size);
223 : }
224 :
225 :
226 : /**
227 : * Do a pre-flight check that we are not in an uncommitted transaction.
228 : * If we are, die.
229 : * Does not return anything, as we will continue regardless of the outcome.
230 : *
231 : * @param cls the `struct PostgresClosure` with the plugin-specific state
232 : */
233 : static void
234 0 : postgres_preflight (void *cls)
235 : {
236 0 : struct PostgresClosure *pg = cls;
237 :
238 0 : if (NULL == pg->transaction_name)
239 0 : return; /* all good */
240 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
241 : "BUG: Preflight check detected running transaction `%s'!\n",
242 : pg->transaction_name);
243 0 : GNUNET_assert (0);
244 : }
245 :
246 :
247 : /**
248 : * Check that the database connection is still up
249 : * and automatically reconnects unless we are
250 : * already inside of a transaction.
251 : *
252 : * @param pg connection to check
253 : */
254 : static void
255 0 : check_connection (struct PostgresClosure *pg)
256 : {
257 0 : if (NULL != pg->transaction_name)
258 0 : return;
259 0 : GNUNET_PQ_reconnect_if_down (pg->conn);
260 : }
261 :
262 :
263 : /**
264 : * Start a transaction.
265 : *
266 : * @param cls the `struct PostgresClosure` with the plugin-specific state
267 : * @param name unique name identifying the transaction (for debugging),
268 : * must point to a constant
269 : * @return #GNUNET_OK on success
270 : */
271 : static enum GNUNET_GenericReturnValue
272 0 : postgres_start (void *cls,
273 : const char *name)
274 : {
275 0 : struct PostgresClosure *pg = cls;
276 0 : struct GNUNET_PQ_ExecuteStatement es[] = {
277 0 : GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
278 : GNUNET_PQ_EXECUTE_STATEMENT_END
279 : };
280 :
281 0 : check_connection (pg);
282 0 : postgres_preflight (pg);
283 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284 : "Starting merchant DB transaction `%s'\n",
285 : name);
286 0 : if (GNUNET_OK !=
287 0 : GNUNET_PQ_exec_statements (pg->conn,
288 : es))
289 : {
290 0 : TALER_LOG_ERROR ("Failed to start transaction\n");
291 0 : GNUNET_break (0);
292 0 : return GNUNET_SYSERR;
293 : }
294 0 : pg->transaction_name = name;
295 0 : return GNUNET_OK;
296 : }
297 :
298 :
299 : /**
300 : * Start a transaction in 'read committed' mode.
301 : *
302 : * @param cls the `struct PostgresClosure` with the plugin-specific state
303 : * @param name unique name identifying the transaction (for debugging),
304 : * must point to a constant
305 : * @return #GNUNET_OK on success
306 : */
307 : static enum GNUNET_GenericReturnValue
308 0 : postgres_start_read_committed (void *cls,
309 : const char *name)
310 : {
311 0 : struct PostgresClosure *pg = cls;
312 0 : struct GNUNET_PQ_ExecuteStatement es[] = {
313 0 : GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
314 : GNUNET_PQ_EXECUTE_STATEMENT_END
315 : };
316 :
317 0 : check_connection (pg);
318 0 : postgres_preflight (pg);
319 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
320 : "Starting merchant DB transaction %s (READ COMMITTED)\n",
321 : name);
322 0 : if (GNUNET_OK !=
323 0 : GNUNET_PQ_exec_statements (pg->conn,
324 : es))
325 : {
326 0 : TALER_LOG_ERROR ("Failed to start transaction\n");
327 0 : GNUNET_break (0);
328 0 : return GNUNET_SYSERR;
329 : }
330 0 : pg->transaction_name = name;
331 0 : return GNUNET_OK;
332 : }
333 :
334 :
335 : /**
336 : * Roll back the current transaction of a database connection.
337 : *
338 : * @param cls the `struct PostgresClosure` with the plugin-specific state
339 : */
340 : static void
341 0 : postgres_rollback (void *cls)
342 : {
343 0 : struct PostgresClosure *pg = cls;
344 0 : struct GNUNET_PQ_ExecuteStatement es[] = {
345 0 : GNUNET_PQ_make_execute ("ROLLBACK"),
346 : GNUNET_PQ_EXECUTE_STATEMENT_END
347 : };
348 :
349 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
350 : "Rolling back merchant DB transaction `%s'\n",
351 : pg->transaction_name);
352 0 : GNUNET_break (GNUNET_OK ==
353 : GNUNET_PQ_exec_statements (pg->conn,
354 : es));
355 0 : pg->transaction_name = NULL;
356 0 : }
357 :
358 :
359 : /**
360 : * Commit the current transaction of a database connection.
361 : *
362 : * @param cls the `struct PostgresClosure` with the plugin-specific state
363 : * @return transaction status code
364 : */
365 : static enum GNUNET_DB_QueryStatus
366 0 : postgres_commit (void *cls)
367 : {
368 0 : struct PostgresClosure *pg = cls;
369 0 : struct GNUNET_PQ_QueryParam params[] = {
370 : GNUNET_PQ_query_param_end
371 : };
372 :
373 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
374 : "Committing merchant DB transaction %s\n",
375 : pg->transaction_name);
376 0 : pg->transaction_name = NULL;
377 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
378 : "end_transaction",
379 : params);
380 : }
381 :
382 :
383 : /**
384 : * Context for lookup_instances().
385 : */
386 : struct LookupInstancesContext
387 : {
388 : /**
389 : * Function to call with the results.
390 : */
391 : TALER_MERCHANTDB_InstanceCallback cb;
392 :
393 : /**
394 : * Closure for @e cb.
395 : */
396 : void *cb_cls;
397 :
398 : /**
399 : * Database context.
400 : */
401 : struct PostgresClosure *pg;
402 :
403 : /**
404 : * Instance settings, valid only during find_instances_cb().
405 : */
406 : struct TALER_MERCHANTDB_InstanceSettings is;
407 :
408 : /**
409 : * Instance authentication settings, valid only during find_instances_cb().
410 : */
411 : struct TALER_MERCHANTDB_InstanceAuthSettings ias;
412 :
413 : /**
414 : * Instance serial number, valid only during find_instances_cb().
415 : */
416 : uint64_t instance_serial;
417 :
418 : /**
419 : * Public key of the current instance, valid only during find_instances_cb().
420 : */
421 : struct TALER_MerchantPublicKeyP merchant_pub;
422 :
423 : /**
424 : * Set to the return value on errors.
425 : */
426 : enum GNUNET_DB_QueryStatus qs;
427 :
428 : /**
429 : * true if we only are interested in instances for which we have the private key.
430 : */
431 : bool active_only;
432 : };
433 :
434 :
435 : /**
436 : * We are processing an instances lookup and have the @a accounts.
437 : * Find the private key if possible, and invoke the callback.
438 : *
439 : * @param lic context we are handling
440 : * @param num_accounts length of @a accounts array
441 : * @param accounts information about accounts of the instance in @a lic
442 : */
443 : static void
444 0 : call_with_accounts (struct LookupInstancesContext *lic,
445 : unsigned int num_accounts,
446 : const struct TALER_MERCHANTDB_AccountDetails accounts[])
447 : {
448 0 : struct PostgresClosure *pg = lic->pg;
449 : enum GNUNET_DB_QueryStatus qs;
450 0 : struct GNUNET_PQ_QueryParam params[] = {
451 0 : GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
452 : GNUNET_PQ_query_param_end
453 : };
454 : struct TALER_MerchantPrivateKeyP merchant_priv;
455 0 : struct GNUNET_PQ_ResultSpec rs[] = {
456 0 : GNUNET_PQ_result_spec_auto_from_type ("merchant_priv",
457 : &merchant_priv),
458 : GNUNET_PQ_result_spec_end
459 : };
460 :
461 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
462 : "lookup_instance_private_key",
463 : params,
464 : rs);
465 0 : if (qs < 0)
466 : {
467 0 : GNUNET_break (0);
468 0 : lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
469 0 : return;
470 : }
471 0 : if ( (0 == qs) &&
472 0 : (lic->active_only) )
473 0 : return; /* skip, not interesting */
474 0 : lic->cb (lic->cb_cls,
475 0 : &lic->merchant_pub,
476 : (0 == qs) ? NULL : &merchant_priv,
477 0 : &lic->is,
478 0 : &lic->ias,
479 : num_accounts,
480 : accounts);
481 : }
482 :
483 :
484 : /**
485 : * Function to be called with the results of a SELECT statement
486 : * that has returned @a num_results results about accounts.
487 : *
488 : * @param cls of type `struct FindInstancesContext *`
489 : * @param result the postgres result
490 : * @param num_results the number of results in @a result
491 : */
492 : static void
493 0 : lookup_accounts_cb (void *cls,
494 : PGresult *result,
495 : unsigned int num_results)
496 0 : {
497 0 : struct LookupInstancesContext *lic = cls;
498 0 : char *paytos[num_results];
499 0 : struct TALER_MERCHANTDB_AccountDetails accounts[num_results];
500 :
501 0 : for (unsigned int i = 0; i < num_results; i++)
502 : {
503 : uint8_t active;
504 0 : struct GNUNET_PQ_ResultSpec rs[] = {
505 0 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
506 : &accounts[i].h_wire),
507 0 : GNUNET_PQ_result_spec_auto_from_type ("salt",
508 : &accounts[i].salt),
509 0 : GNUNET_PQ_result_spec_string ("payto_uri",
510 : &paytos[i]),
511 0 : GNUNET_PQ_result_spec_auto_from_type ("active",
512 : &active),
513 : GNUNET_PQ_result_spec_end
514 : };
515 :
516 0 : if (GNUNET_OK !=
517 0 : GNUNET_PQ_extract_result (result,
518 : rs,
519 : i))
520 : {
521 0 : GNUNET_break (0);
522 0 : lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
523 0 : for (unsigned int j = 0; j < i; j++)
524 0 : GNUNET_free (paytos[j]);
525 0 : return;
526 : }
527 0 : accounts[i].active = (0 != active);
528 0 : accounts[i].payto_uri = paytos[i];
529 : }
530 0 : call_with_accounts (lic,
531 : num_results,
532 : accounts);
533 0 : for (unsigned int i = 0; i < num_results; i++)
534 0 : GNUNET_free (paytos[i]);
535 : }
536 :
537 :
538 : /**
539 : * Function to be called with the results of a SELECT statement
540 : * that has returned @a num_results results about instances.
541 : *
542 : * @param cls of type `struct FindInstancesContext *`
543 : * @param result the postgres result
544 : * @param num_results the number of results in @a result
545 : */
546 : static void
547 0 : lookup_instances_cb (void *cls,
548 : PGresult *result,
549 : unsigned int num_results)
550 : {
551 0 : struct LookupInstancesContext *lic = cls;
552 0 : struct PostgresClosure *pg = lic->pg;
553 :
554 0 : for (unsigned int i = 0; i < num_results; i++)
555 : {
556 : bool no_auth;
557 : bool no_salt;
558 0 : struct GNUNET_PQ_ResultSpec rs[] = {
559 0 : GNUNET_PQ_result_spec_uint64 ("merchant_serial",
560 : &lic->instance_serial),
561 0 : GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
562 : &lic->merchant_pub),
563 0 : GNUNET_PQ_result_spec_allow_null (
564 0 : GNUNET_PQ_result_spec_auto_from_type ("auth_hash",
565 : &lic->ias.auth_hash),
566 : &no_auth),
567 0 : GNUNET_PQ_result_spec_allow_null (
568 0 : GNUNET_PQ_result_spec_auto_from_type ("auth_salt",
569 : &lic->ias.auth_salt),
570 : &no_salt),
571 0 : GNUNET_PQ_result_spec_string ("merchant_id",
572 : &lic->is.id),
573 0 : GNUNET_PQ_result_spec_string ("merchant_name",
574 : &lic->is.name),
575 0 : TALER_PQ_result_spec_json ("address",
576 : &lic->is.address),
577 0 : TALER_PQ_result_spec_json ("jurisdiction",
578 : &lic->is.jurisdiction),
579 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_deposit_fee",
580 : &lic->is.default_max_deposit_fee),
581 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_wire_fee",
582 : &lic->is.default_max_wire_fee),
583 0 : GNUNET_PQ_result_spec_uint32 ("default_wire_fee_amortization",
584 : &lic->is.default_wire_fee_amortization),
585 0 : GNUNET_PQ_result_spec_relative_time ("default_wire_transfer_delay",
586 : &lic->is.default_wire_transfer_delay),
587 0 : GNUNET_PQ_result_spec_relative_time ("default_pay_delay",
588 : &lic->is.default_pay_delay),
589 0 : GNUNET_PQ_result_spec_allow_null (
590 : GNUNET_PQ_result_spec_string ("website",
591 : &lic->is.website),
592 : NULL),
593 0 : GNUNET_PQ_result_spec_allow_null (
594 : GNUNET_PQ_result_spec_string ("email",
595 : &lic->is.email),
596 : NULL),
597 0 : GNUNET_PQ_result_spec_allow_null (
598 : GNUNET_PQ_result_spec_string ("logo",
599 : &lic->is.logo),
600 : NULL),
601 : GNUNET_PQ_result_spec_end
602 : };
603 0 : struct GNUNET_PQ_QueryParam params[] = {
604 0 : GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
605 : GNUNET_PQ_query_param_end
606 : };
607 :
608 0 : memset (&lic->ias.auth_salt,
609 : 0,
610 : sizeof (lic->ias.auth_salt));
611 0 : memset (&lic->ias.auth_hash,
612 : 0,
613 : sizeof (lic->ias.auth_hash));
614 0 : if (GNUNET_OK !=
615 0 : GNUNET_PQ_extract_result (result,
616 : rs,
617 : i))
618 : {
619 0 : GNUNET_break (0);
620 0 : lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
621 0 : return;
622 : }
623 0 : lic->qs = GNUNET_PQ_eval_prepared_multi_select (lic->pg->conn,
624 : "lookup_accounts",
625 : params,
626 : &lookup_accounts_cb,
627 : lic);
628 0 : if (0 > lic->qs)
629 : {
630 : /* lookup_accounts_cb() did not run, still notify about the
631 : account-less instance! */
632 0 : call_with_accounts (lic,
633 : 0,
634 : NULL);
635 : }
636 0 : GNUNET_PQ_cleanup_result (rs);
637 0 : if (0 > lic->qs)
638 0 : break;
639 : }
640 : }
641 :
642 :
643 : /**
644 : * Lookup all of the instances this backend has configured.
645 : *
646 : * @param cls closure
647 : * @param active_only only find 'active' instances
648 : * @param cb function to call on all instances found
649 : * @param cb_cls closure for @a cb
650 : */
651 : static enum GNUNET_DB_QueryStatus
652 0 : postgres_lookup_instances (void *cls,
653 : bool active_only,
654 : TALER_MERCHANTDB_InstanceCallback cb,
655 : void *cb_cls)
656 : {
657 0 : struct PostgresClosure *pg = cls;
658 0 : struct LookupInstancesContext lic = {
659 : .cb = cb,
660 : .cb_cls = cb_cls,
661 : .active_only = active_only,
662 : .pg = pg
663 : };
664 0 : struct GNUNET_PQ_QueryParam params[] = {
665 : GNUNET_PQ_query_param_end
666 : };
667 : enum GNUNET_DB_QueryStatus qs;
668 :
669 0 : check_connection (pg);
670 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
671 : "lookup_instances",
672 : params,
673 : &lookup_instances_cb,
674 : &lic);
675 0 : if (0 > lic.qs)
676 0 : return lic.qs;
677 0 : return qs;
678 : }
679 :
680 :
681 : /**
682 : * Lookup all one of the instances this backend has configured.
683 : *
684 : * @param cls closure
685 : * @param id instance ID to resolve
686 : * @param active_only only find 'active' instances
687 : * @param cb function to call on all instances found
688 : * @param cb_cls closure for @a cb
689 : */
690 : static enum GNUNET_DB_QueryStatus
691 0 : postgres_lookup_instance (void *cls,
692 : const char *id,
693 : bool active_only,
694 : TALER_MERCHANTDB_InstanceCallback cb,
695 : void *cb_cls)
696 : {
697 0 : struct PostgresClosure *pg = cls;
698 0 : struct LookupInstancesContext lic = {
699 : .cb = cb,
700 : .cb_cls = cb_cls,
701 : .active_only = active_only,
702 : .pg = pg
703 : };
704 0 : struct GNUNET_PQ_QueryParam params[] = {
705 0 : GNUNET_PQ_query_param_string (id),
706 : GNUNET_PQ_query_param_end
707 : };
708 : enum GNUNET_DB_QueryStatus qs;
709 :
710 0 : check_connection (pg);
711 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
712 : "lookup_instance",
713 : params,
714 : &lookup_instances_cb,
715 : &lic);
716 0 : if (0 > lic.qs)
717 0 : return lic.qs;
718 0 : return qs;
719 : }
720 :
721 :
722 : /**
723 : * Lookup authentication data of an instance.
724 : *
725 : * @param cls closure
726 : * @param instance_id instance to query
727 : * @param[out] ias where to store the auth data
728 : */
729 : static enum GNUNET_DB_QueryStatus
730 0 : postgres_lookup_instance_auth (
731 : void *cls,
732 : const char *instance_id,
733 : struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
734 : {
735 0 : struct PostgresClosure *pg = cls;
736 0 : struct GNUNET_PQ_QueryParam params[] = {
737 0 : GNUNET_PQ_query_param_string (instance_id),
738 : GNUNET_PQ_query_param_end
739 : };
740 0 : struct GNUNET_PQ_ResultSpec rs[] = {
741 0 : GNUNET_PQ_result_spec_auto_from_type ("auth_hash",
742 : &ias->auth_hash),
743 0 : GNUNET_PQ_result_spec_auto_from_type ("auth_salt",
744 : &ias->auth_salt),
745 : GNUNET_PQ_result_spec_end
746 : };
747 :
748 0 : check_connection (pg);
749 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
750 : "lookup_instance_auth",
751 : params,
752 : rs);
753 : }
754 :
755 :
756 : /**
757 : * Insert information about an instance into our database.
758 : *
759 : * @param cls closure
760 : * @param merchant_pub public key of the instance
761 : * @param merchant_priv private key of the instance
762 : * @param is details about the instance
763 : * @param ias authentication settings for the instance
764 : * @return database result code
765 : */
766 : static enum GNUNET_DB_QueryStatus
767 0 : postgres_insert_instance (
768 : void *cls,
769 : const struct TALER_MerchantPublicKeyP *merchant_pub,
770 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
771 : const struct TALER_MERCHANTDB_InstanceSettings *is,
772 : const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
773 : {
774 0 : struct PostgresClosure *pg = cls;
775 0 : struct GNUNET_PQ_QueryParam params[] = {
776 0 : GNUNET_PQ_query_param_auto_from_type (merchant_pub),
777 0 : GNUNET_PQ_query_param_auto_from_type (&ias->auth_hash),
778 0 : GNUNET_PQ_query_param_auto_from_type (&ias->auth_salt),
779 0 : GNUNET_PQ_query_param_string (is->id),
780 0 : GNUNET_PQ_query_param_string (is->name),
781 0 : TALER_PQ_query_param_json (is->address),
782 0 : TALER_PQ_query_param_json (is->jurisdiction),
783 0 : TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
784 0 : TALER_PQ_query_param_amount (&is->default_max_wire_fee),
785 0 : GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
786 0 : GNUNET_PQ_query_param_relative_time (
787 : &is->default_wire_transfer_delay),
788 0 : GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
789 0 : (NULL == is->website)
790 0 : ? GNUNET_PQ_query_param_null ()
791 0 : : GNUNET_PQ_query_param_string (is->website),
792 0 : (NULL == is->email)
793 0 : ? GNUNET_PQ_query_param_null ()
794 0 : : GNUNET_PQ_query_param_string (is->email),
795 0 : (NULL == is->logo)
796 0 : ? GNUNET_PQ_query_param_null ()
797 0 : : GNUNET_PQ_query_param_string (is->logo),
798 : GNUNET_PQ_query_param_end
799 : };
800 0 : struct GNUNET_PQ_QueryParam params_priv[] = {
801 0 : GNUNET_PQ_query_param_auto_from_type (merchant_priv),
802 0 : GNUNET_PQ_query_param_string (is->id),
803 : GNUNET_PQ_query_param_end
804 : };
805 : enum GNUNET_DB_QueryStatus qs;
806 :
807 0 : check_connection (pg);
808 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
809 : "insert_instance",
810 : params);
811 0 : if (qs <= 0)
812 0 : return qs;
813 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
814 : "insert_keys",
815 : params_priv);
816 : }
817 :
818 :
819 : /**
820 : * Insert information about an instance's account into our database.
821 : *
822 : * @param cls closure
823 : * @param id identifier of the instance
824 : * @param account_details details about the account
825 : * @return database result code
826 : */
827 : static enum GNUNET_DB_QueryStatus
828 0 : postgres_insert_account (
829 : void *cls,
830 : const char *id,
831 : const struct TALER_MERCHANTDB_AccountDetails *account_details)
832 : {
833 0 : struct PostgresClosure *pg = cls;
834 0 : struct GNUNET_PQ_QueryParam params[] = {
835 0 : GNUNET_PQ_query_param_string (id),
836 0 : GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire),
837 0 : GNUNET_PQ_query_param_auto_from_type (&account_details->salt),
838 0 : GNUNET_PQ_query_param_string (account_details->payto_uri),
839 0 : GNUNET_PQ_query_param_bool (account_details->active),
840 : GNUNET_PQ_query_param_end
841 : };
842 :
843 0 : check_connection (pg);
844 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
845 : "insert_account",
846 : params);
847 : }
848 :
849 :
850 : /**
851 : * Closure for kyc_status_cb().
852 : */
853 : struct KycStatusContext
854 : {
855 : /**
856 : * Function to call with results.
857 : */
858 : TALER_MERCHANTDB_KycCallback kyc_cb;
859 :
860 : /**
861 : * Closure for @e kyc_cb.
862 : */
863 : void *kyc_cb_cls;
864 :
865 : /**
866 : * Filter, NULL to not filter.
867 : */
868 : const struct TALER_MerchantWireHashP *h_wire;
869 :
870 : /**
871 : * Filter, NULL to not filter.
872 : */
873 : const char *exchange_url;
874 :
875 : /**
876 : * Number of results found.
877 : */
878 : unsigned int count;
879 :
880 : /**
881 : * Set to true on failure(s).
882 : */
883 : bool failure;
884 : };
885 :
886 :
887 : /**
888 : * Function to be called with the results of a SELECT statement
889 : * that has returned @a num_results results about accounts.
890 : *
891 : * @param[in,out] cls of type `struct KycStatusContext *`
892 : * @param result the postgres result
893 : * @param num_results the number of results in @a result
894 : */
895 : static void
896 0 : kyc_status_cb (void *cls,
897 : PGresult *result,
898 : unsigned int num_results)
899 : {
900 0 : struct KycStatusContext *ksc = cls;
901 :
902 0 : for (unsigned int i = 0; i < num_results; i++)
903 : {
904 : struct TALER_MerchantWireHashP h_wire;
905 : uint64_t kyc_serial;
906 : char *exchange_url;
907 : char *payto_uri;
908 : struct GNUNET_TIME_Timestamp last_check;
909 : uint8_t kyc_ok;
910 0 : struct GNUNET_PQ_ResultSpec rs[] = {
911 0 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
912 : &h_wire),
913 0 : GNUNET_PQ_result_spec_uint64 ("exchange_kyc_serial",
914 : &kyc_serial),
915 0 : GNUNET_PQ_result_spec_string ("payto_uri",
916 : &payto_uri),
917 0 : GNUNET_PQ_result_spec_string ("exchange_url",
918 : &exchange_url),
919 0 : GNUNET_PQ_result_spec_timestamp ("kyc_timestamp",
920 : &last_check),
921 0 : GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
922 : &kyc_ok),
923 : GNUNET_PQ_result_spec_end
924 : };
925 :
926 0 : if (GNUNET_OK !=
927 0 : GNUNET_PQ_extract_result (result,
928 : rs,
929 : i))
930 : {
931 0 : GNUNET_break (0);
932 0 : ksc->failure = true;
933 0 : return;
934 : }
935 0 : if ( (NULL != ksc->exchange_url) &&
936 0 : (0 != strcmp (ksc->exchange_url,
937 : exchange_url)) )
938 : {
939 0 : GNUNET_PQ_cleanup_result (rs);
940 0 : continue;
941 : }
942 0 : if ( (NULL != ksc->h_wire) &&
943 0 : (0 != GNUNET_memcmp (ksc->h_wire,
944 : &h_wire)) )
945 : {
946 0 : GNUNET_PQ_cleanup_result (rs);
947 0 : continue;
948 : }
949 0 : ksc->count++;
950 0 : ksc->kyc_cb (ksc->kyc_cb_cls,
951 : &h_wire,
952 : kyc_serial,
953 : payto_uri,
954 : exchange_url,
955 : last_check,
956 : 0 != kyc_ok);
957 0 : GNUNET_PQ_cleanup_result (rs);
958 : }
959 : }
960 :
961 :
962 : /**
963 : * Check an instance's account's KYC status.
964 : *
965 : * @param cls closure
966 : * @param merchant_id merchant backend instance ID
967 : * @param h_wire hash of the wire account to check,
968 : * NULL to check all accounts of the merchant
969 : * @param exchange_url base URL of the exchange to check,
970 : * NULL to check all exchanges
971 : * @param kyc_cb KYC status callback to invoke
972 : * @param kyc_cb_cls closure for @a kyc_cb
973 : * @return database result code
974 : */
975 : static enum GNUNET_DB_QueryStatus
976 0 : postgres_account_kyc_get_status (void *cls,
977 : const char *merchant_id,
978 : const struct TALER_MerchantWireHashP *h_wire,
979 : const char *exchange_url,
980 : TALER_MERCHANTDB_KycCallback kyc_cb,
981 : void *kyc_cb_cls)
982 : {
983 0 : struct PostgresClosure *pg = cls;
984 0 : struct KycStatusContext ksc = {
985 : .kyc_cb = kyc_cb,
986 : .kyc_cb_cls = kyc_cb_cls,
987 : .exchange_url = exchange_url,
988 : .h_wire = h_wire
989 : };
990 0 : struct GNUNET_PQ_QueryParam params[] = {
991 0 : GNUNET_PQ_query_param_string (merchant_id),
992 : GNUNET_PQ_query_param_end
993 : };
994 : enum GNUNET_DB_QueryStatus qs;
995 :
996 0 : check_connection (pg);
997 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
998 : "lookup_kyc_status",
999 : params,
1000 : &kyc_status_cb,
1001 : &ksc);
1002 0 : if (ksc.failure)
1003 : {
1004 0 : GNUNET_break (0);
1005 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1006 : }
1007 0 : if (0 > qs)
1008 0 : return qs;
1009 0 : return ksc.count;
1010 : }
1011 :
1012 :
1013 : /**
1014 : * Update an instance's account's KYC status.
1015 : *
1016 : * @param cls closure
1017 : * @param merchant_id merchant backend instance ID
1018 : * @param h_wire hash of the wire account to check
1019 : * @param exchange_url base URL of the exchange to check
1020 : * @param exchange_kyc_serial serial number for our account at the exchange (0 if unknown)
1021 : * @param exchange_sig signature of the exchange, or NULL for none
1022 : * @param exchange_pub public key of the exchange, or NULL for none
1023 : * @param timestamp timestamp to store
1024 : * @param kyc_ok current KYC status (true for satisfied)
1025 : * @return database result code
1026 : */
1027 : static enum GNUNET_DB_QueryStatus
1028 0 : postgres_account_kyc_set_status (
1029 : void *cls,
1030 : const char *merchant_id,
1031 : const struct TALER_MerchantWireHashP *h_wire,
1032 : const char *exchange_url,
1033 : uint64_t exchange_kyc_serial,
1034 : const struct TALER_ExchangeSignatureP *exchange_sig,
1035 : const struct TALER_ExchangePublicKeyP *exchange_pub,
1036 : struct GNUNET_TIME_Timestamp timestamp,
1037 : bool kyc_ok)
1038 : {
1039 0 : struct PostgresClosure *pg = cls;
1040 0 : uint8_t ok = kyc_ok;
1041 0 : struct GNUNET_PQ_QueryParam params[] = {
1042 0 : GNUNET_PQ_query_param_string (merchant_id),
1043 0 : GNUNET_PQ_query_param_auto_from_type (h_wire),
1044 0 : GNUNET_PQ_query_param_string (exchange_url),
1045 0 : GNUNET_PQ_query_param_uint64 (&exchange_kyc_serial),
1046 0 : GNUNET_PQ_query_param_timestamp (×tamp),
1047 0 : GNUNET_PQ_query_param_auto_from_type (&ok),
1048 : exchange_pub
1049 0 : ? GNUNET_PQ_query_param_auto_from_type (exchange_pub)
1050 0 : : GNUNET_PQ_query_param_null (),
1051 : exchange_sig
1052 0 : ? GNUNET_PQ_query_param_auto_from_type (exchange_sig)
1053 0 : : GNUNET_PQ_query_param_null (),
1054 : GNUNET_PQ_query_param_end
1055 : };
1056 :
1057 0 : check_connection (pg);
1058 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1059 : "upsert_account_kyc",
1060 : params);
1061 : }
1062 :
1063 :
1064 : /**
1065 : * Delete private key of an instance from our database.
1066 : *
1067 : * @param cls closure
1068 : * @param merchant_id identifier of the instance
1069 : * @return database result code
1070 : */
1071 : static enum GNUNET_DB_QueryStatus
1072 0 : postgres_delete_instance_private_key (
1073 : void *cls,
1074 : const char *merchant_id)
1075 : {
1076 0 : struct PostgresClosure *pg = cls;
1077 0 : struct GNUNET_PQ_QueryParam params[] = {
1078 0 : GNUNET_PQ_query_param_string (merchant_id),
1079 : GNUNET_PQ_query_param_end
1080 : };
1081 :
1082 0 : check_connection (pg);
1083 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1084 : "delete_key",
1085 : params);
1086 : }
1087 :
1088 :
1089 : /**
1090 : * Purge an instance and all associated information from our database.
1091 : * Highly likely to cause undesired data loss. Use with caution.
1092 : *
1093 : * @param cls closure
1094 : * @param merchant_id identifier of the instance
1095 : * @return database result code
1096 : */
1097 : static enum GNUNET_DB_QueryStatus
1098 0 : postgres_purge_instance (void *cls,
1099 : const char *merchant_id)
1100 : {
1101 0 : struct PostgresClosure *pg = cls;
1102 0 : struct GNUNET_PQ_QueryParam params[] = {
1103 0 : GNUNET_PQ_query_param_string (merchant_id),
1104 : GNUNET_PQ_query_param_end
1105 : };
1106 :
1107 0 : check_connection (pg);
1108 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1109 : "purge_instance",
1110 : params);
1111 : }
1112 :
1113 :
1114 : /**
1115 : * Update information about an instance into our database.
1116 : *
1117 : * @param cls closure
1118 : * @param is details about the instance
1119 : * @return database result code
1120 : */
1121 : static enum GNUNET_DB_QueryStatus
1122 0 : postgres_update_instance (void *cls,
1123 : const struct TALER_MERCHANTDB_InstanceSettings *is)
1124 : {
1125 0 : struct PostgresClosure *pg = cls;
1126 0 : struct GNUNET_PQ_QueryParam params[] = {
1127 0 : GNUNET_PQ_query_param_string (is->id),
1128 0 : GNUNET_PQ_query_param_string (is->name),
1129 0 : TALER_PQ_query_param_json (is->address),
1130 0 : TALER_PQ_query_param_json (is->jurisdiction),
1131 0 : TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
1132 0 : TALER_PQ_query_param_amount (&is->default_max_wire_fee),
1133 0 : GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
1134 0 : GNUNET_PQ_query_param_relative_time (
1135 : &is->default_wire_transfer_delay),
1136 0 : GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
1137 0 : (NULL == is->website)
1138 0 : ? GNUNET_PQ_query_param_null ()
1139 0 : : GNUNET_PQ_query_param_string (is->website),
1140 0 : (NULL == is->email)
1141 0 : ? GNUNET_PQ_query_param_null ()
1142 0 : : GNUNET_PQ_query_param_string (is->email),
1143 0 : (NULL == is->logo)
1144 0 : ? GNUNET_PQ_query_param_null ()
1145 0 : : GNUNET_PQ_query_param_string (is->logo),
1146 : GNUNET_PQ_query_param_end
1147 : };
1148 :
1149 0 : check_connection (pg);
1150 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1151 : "update_instance",
1152 : params);
1153 : }
1154 :
1155 :
1156 : /**
1157 : * Update information about an instance's authentication settings
1158 : * into our database.
1159 : *
1160 : * @param cls closure
1161 : * @param merchant_id identity of the instance
1162 : * @param is authentication details about the instance
1163 : * @return database result code
1164 : */
1165 : static enum GNUNET_DB_QueryStatus
1166 0 : postgres_update_instance_auth (
1167 : void *cls,
1168 : const char *merchant_id,
1169 : const struct TALER_MERCHANTDB_InstanceAuthSettings *is)
1170 : {
1171 0 : struct PostgresClosure *pg = cls;
1172 0 : struct GNUNET_PQ_QueryParam params[] = {
1173 0 : GNUNET_PQ_query_param_string (merchant_id),
1174 0 : GNUNET_PQ_query_param_auto_from_type (&is->auth_hash),
1175 0 : GNUNET_PQ_query_param_auto_from_type (&is->auth_salt),
1176 : GNUNET_PQ_query_param_end
1177 : };
1178 :
1179 0 : check_connection (pg);
1180 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1181 : "update_instance_auth",
1182 : params);
1183 : }
1184 :
1185 :
1186 : /**
1187 : * Set an instance's account in our database to "inactive".
1188 : *
1189 : * @param cls closure
1190 : * @param merchant_id merchant backend instance ID
1191 : * @param h_wire hash of the wire account to set to inactive
1192 : * @return database result code
1193 : */
1194 : static enum GNUNET_DB_QueryStatus
1195 0 : postgres_inactivate_account (void *cls,
1196 : const char *merchant_id,
1197 : const struct TALER_MerchantWireHashP *h_wire)
1198 : {
1199 0 : struct PostgresClosure *pg = cls;
1200 0 : struct GNUNET_PQ_QueryParam params[] = {
1201 0 : GNUNET_PQ_query_param_string (merchant_id),
1202 0 : GNUNET_PQ_query_param_auto_from_type (h_wire),
1203 : GNUNET_PQ_query_param_end
1204 : };
1205 :
1206 0 : check_connection (pg);
1207 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1208 : "inactivate_account",
1209 : params);
1210 : }
1211 :
1212 :
1213 : /**
1214 : * Set an instance's account in our database to "active".
1215 : *
1216 : * @param cls closure
1217 : * @param merchant_id merchant backend instance ID
1218 : * @param h_wire hash of the wire account to set to active
1219 : * @return database result code
1220 : */
1221 : static enum GNUNET_DB_QueryStatus
1222 0 : postgres_activate_account (void *cls,
1223 : const char *merchant_id,
1224 : const struct TALER_MerchantWireHashP *h_wire)
1225 : {
1226 0 : struct PostgresClosure *pg = cls;
1227 0 : struct GNUNET_PQ_QueryParam params[] = {
1228 0 : GNUNET_PQ_query_param_string (merchant_id),
1229 0 : GNUNET_PQ_query_param_auto_from_type (h_wire),
1230 : GNUNET_PQ_query_param_end
1231 : };
1232 :
1233 0 : check_connection (pg);
1234 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1235 : "activate_account",
1236 : params);
1237 : }
1238 :
1239 :
1240 : /**
1241 : * Context used for postgres_lookup_products().
1242 : */
1243 : struct LookupProductsContext
1244 : {
1245 : /**
1246 : * Function to call with the results.
1247 : */
1248 : TALER_MERCHANTDB_ProductsCallback cb;
1249 :
1250 : /**
1251 : * Closure for @a cb.
1252 : */
1253 : void *cb_cls;
1254 :
1255 : /**
1256 : * Did database result extraction fail?
1257 : */
1258 : bool extract_failed;
1259 : };
1260 :
1261 :
1262 : /**
1263 : * Function to be called with the results of a SELECT statement
1264 : * that has returned @a num_results results about products.
1265 : *
1266 : * @param[in,out] cls of type `struct LookupProductsContext *`
1267 : * @param result the postgres result
1268 : * @param num_results the number of results in @a result
1269 : */
1270 : static void
1271 0 : lookup_products_cb (void *cls,
1272 : PGresult *result,
1273 : unsigned int num_results)
1274 : {
1275 0 : struct LookupProductsContext *plc = cls;
1276 :
1277 0 : for (unsigned int i = 0; i < num_results; i++)
1278 : {
1279 : char *product_id;
1280 0 : struct GNUNET_PQ_ResultSpec rs[] = {
1281 0 : GNUNET_PQ_result_spec_string ("product_id",
1282 : &product_id),
1283 : GNUNET_PQ_result_spec_end
1284 : };
1285 :
1286 0 : if (GNUNET_OK !=
1287 0 : GNUNET_PQ_extract_result (result,
1288 : rs,
1289 : i))
1290 : {
1291 0 : GNUNET_break (0);
1292 0 : plc->extract_failed = true;
1293 0 : return;
1294 : }
1295 0 : plc->cb (plc->cb_cls,
1296 : product_id);
1297 0 : GNUNET_PQ_cleanup_result (rs);
1298 : }
1299 : }
1300 :
1301 :
1302 : /**
1303 : * Lookup all of the products the given instance has configured.
1304 : *
1305 : * @param cls closure
1306 : * @param instance_id instance to lookup products for
1307 : * @param cb function to call on all products found
1308 : * @param cb_cls closure for @a cb
1309 : * @return database result code
1310 : */
1311 : static enum GNUNET_DB_QueryStatus
1312 0 : postgres_lookup_products (void *cls,
1313 : const char *instance_id,
1314 : TALER_MERCHANTDB_ProductsCallback cb,
1315 : void *cb_cls)
1316 : {
1317 0 : struct PostgresClosure *pg = cls;
1318 0 : struct LookupProductsContext plc = {
1319 : .cb = cb,
1320 : .cb_cls = cb_cls,
1321 : /* Can be overwritten by the lookup_products_cb */
1322 : .extract_failed = false,
1323 : };
1324 0 : struct GNUNET_PQ_QueryParam params[] = {
1325 0 : GNUNET_PQ_query_param_string (instance_id),
1326 : GNUNET_PQ_query_param_end
1327 : };
1328 : enum GNUNET_DB_QueryStatus qs;
1329 :
1330 0 : check_connection (pg);
1331 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
1332 : "lookup_products",
1333 : params,
1334 : &lookup_products_cb,
1335 : &plc);
1336 : /* If there was an error inside lookup_products_cb, return a hard error. */
1337 0 : if (plc.extract_failed)
1338 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1339 0 : return qs;
1340 : }
1341 :
1342 :
1343 : /**
1344 : * Lookup details about a particular product.
1345 : *
1346 : * @param cls closure
1347 : * @param instance_id instance to lookup products for
1348 : * @param product_id product to lookup
1349 : * @param[out] pd set to the product details on success, can be NULL
1350 : * (in that case we only want to check if the product exists)
1351 : * @return database result code
1352 : */
1353 : static enum GNUNET_DB_QueryStatus
1354 0 : postgres_lookup_product (void *cls,
1355 : const char *instance_id,
1356 : const char *product_id,
1357 : struct TALER_MERCHANTDB_ProductDetails *pd)
1358 : {
1359 0 : struct PostgresClosure *pg = cls;
1360 0 : struct GNUNET_PQ_QueryParam params[] = {
1361 0 : GNUNET_PQ_query_param_string (instance_id),
1362 0 : GNUNET_PQ_query_param_string (product_id),
1363 : GNUNET_PQ_query_param_end
1364 : };
1365 :
1366 0 : if (NULL == pd)
1367 : {
1368 0 : struct GNUNET_PQ_ResultSpec rs_null[] = {
1369 : GNUNET_PQ_result_spec_end
1370 : };
1371 :
1372 0 : check_connection (pg);
1373 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1374 : "lookup_product",
1375 : params,
1376 : rs_null);
1377 : }
1378 : else
1379 : {
1380 0 : struct GNUNET_PQ_ResultSpec rs[] = {
1381 0 : GNUNET_PQ_result_spec_string ("description",
1382 : &pd->description),
1383 0 : TALER_PQ_result_spec_json ("description_i18n",
1384 : &pd->description_i18n),
1385 0 : GNUNET_PQ_result_spec_string ("unit",
1386 : &pd->unit),
1387 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("price",
1388 : &pd->price),
1389 0 : TALER_PQ_result_spec_json ("taxes",
1390 : &pd->taxes),
1391 0 : GNUNET_PQ_result_spec_uint64 ("total_stock",
1392 : &pd->total_stock),
1393 0 : GNUNET_PQ_result_spec_uint64 ("total_sold",
1394 : &pd->total_sold),
1395 0 : GNUNET_PQ_result_spec_uint64 ("total_lost",
1396 : &pd->total_lost),
1397 0 : GNUNET_PQ_result_spec_string ("image",
1398 : &pd->image),
1399 0 : TALER_PQ_result_spec_json ("address",
1400 : &pd->address),
1401 0 : GNUNET_PQ_result_spec_timestamp ("next_restock",
1402 : &pd->next_restock),
1403 0 : GNUNET_PQ_result_spec_uint32 ("minimum_age",
1404 : &pd->minimum_age),
1405 : GNUNET_PQ_result_spec_end
1406 : };
1407 :
1408 0 : check_connection (pg);
1409 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1410 : "lookup_product",
1411 : params,
1412 : rs);
1413 : }
1414 : }
1415 :
1416 :
1417 : /**
1418 : * Delete information about a product. Note that the transaction must
1419 : * enforce that no stocks are currently locked.
1420 : *
1421 : * @param cls closure
1422 : * @param instance_id instance to delete product of
1423 : * @param product_id product to delete
1424 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
1425 : * if locks prevent deletion OR product unknown
1426 : */
1427 : static enum GNUNET_DB_QueryStatus
1428 0 : postgres_delete_product (void *cls,
1429 : const char *instance_id,
1430 : const char *product_id)
1431 : {
1432 0 : struct PostgresClosure *pg = cls;
1433 0 : struct GNUNET_PQ_QueryParam params[] = {
1434 0 : GNUNET_PQ_query_param_string (instance_id),
1435 0 : GNUNET_PQ_query_param_string (product_id),
1436 : GNUNET_PQ_query_param_end
1437 : };
1438 :
1439 0 : check_connection (pg);
1440 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1441 : "delete_product",
1442 : params);
1443 : }
1444 :
1445 :
1446 : /**
1447 : * Insert details about a particular product.
1448 : *
1449 : * @param cls closure
1450 : * @param instance_id instance to insert product for
1451 : * @param product_id product identifier of product to insert
1452 : * @param pd the product details to insert
1453 : * @return database result code
1454 : */
1455 : static enum GNUNET_DB_QueryStatus
1456 0 : postgres_insert_product (void *cls,
1457 : const char *instance_id,
1458 : const char *product_id,
1459 : const struct TALER_MERCHANTDB_ProductDetails *pd)
1460 : {
1461 0 : struct PostgresClosure *pg = cls;
1462 0 : struct GNUNET_PQ_QueryParam params[] = {
1463 0 : GNUNET_PQ_query_param_string (instance_id),
1464 0 : GNUNET_PQ_query_param_string (product_id),
1465 0 : GNUNET_PQ_query_param_string (pd->description),
1466 0 : TALER_PQ_query_param_json (pd->description_i18n),
1467 0 : GNUNET_PQ_query_param_string (pd->unit),
1468 0 : GNUNET_PQ_query_param_string (pd->image),
1469 0 : TALER_PQ_query_param_json (pd->taxes),
1470 0 : TALER_PQ_query_param_amount (&pd->price),
1471 0 : GNUNET_PQ_query_param_uint64 (&pd->total_stock),
1472 0 : TALER_PQ_query_param_json (pd->address),
1473 0 : GNUNET_PQ_query_param_timestamp (&pd->next_restock),
1474 0 : GNUNET_PQ_query_param_uint32 (&pd->minimum_age),
1475 : GNUNET_PQ_query_param_end
1476 : };
1477 :
1478 0 : check_connection (pg);
1479 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1480 : "insert_product",
1481 : params);
1482 : }
1483 :
1484 :
1485 : /**
1486 : * Update details about a particular product. Note that the
1487 : * transaction must enforce that the sold/stocked/lost counters
1488 : * are not reduced (i.e. by expanding the WHERE clause on the existing
1489 : * values).
1490 : *
1491 : * @param cls closure
1492 : * @param instance_id instance to lookup products for
1493 : * @param product_id product to lookup
1494 : * @param[out] pd set to the product details on success, can be NULL
1495 : * (in that case we only want to check if the product exists)
1496 : * total_sold in @a pd is ignored, total_lost must not
1497 : * exceed total_stock minus the existing total_sold;
1498 : * total_sold and total_stock must be larger or equal to
1499 : * the existing value;
1500 : * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the
1501 : * non-decreasing constraints are not met *or* if the product
1502 : * does not yet exist.
1503 : */
1504 : static enum GNUNET_DB_QueryStatus
1505 0 : postgres_update_product (void *cls,
1506 : const char *instance_id,
1507 : const char *product_id,
1508 : const struct TALER_MERCHANTDB_ProductDetails *pd)
1509 : {
1510 0 : struct PostgresClosure *pg = cls;
1511 0 : struct GNUNET_PQ_QueryParam params[] = {
1512 0 : GNUNET_PQ_query_param_string (instance_id), /* $1 */
1513 0 : GNUNET_PQ_query_param_string (product_id),
1514 0 : GNUNET_PQ_query_param_string (pd->description),
1515 0 : TALER_PQ_query_param_json (pd->description_i18n),
1516 0 : GNUNET_PQ_query_param_string (pd->unit),
1517 0 : GNUNET_PQ_query_param_string (pd->image), /* $6 */
1518 0 : TALER_PQ_query_param_json (pd->taxes),
1519 0 : TALER_PQ_query_param_amount (&pd->price), /* $8+$9 */
1520 0 : GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $10 */
1521 0 : GNUNET_PQ_query_param_uint64 (&pd->total_lost),
1522 0 : TALER_PQ_query_param_json (pd->address),
1523 0 : GNUNET_PQ_query_param_timestamp (&pd->next_restock),
1524 0 : GNUNET_PQ_query_param_uint32 (&pd->minimum_age),
1525 : GNUNET_PQ_query_param_end
1526 : };
1527 :
1528 0 : if ( (pd->total_stock < pd->total_lost + pd->total_sold) ||
1529 0 : (pd->total_lost < pd->total_lost
1530 0 : + pd->total_sold) /* integer overflow */)
1531 : {
1532 0 : GNUNET_break (0);
1533 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1534 : }
1535 0 : check_connection (pg);
1536 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1537 : "update_product",
1538 : params);
1539 : }
1540 :
1541 :
1542 : /**
1543 : * Lock stocks of a particular product. Note that the transaction must
1544 : * enforce that the "stocked-sold-lost >= locked" constraint holds.
1545 : *
1546 : * @param cls closure
1547 : * @param instance_id instance to lookup products for
1548 : * @param product_id product to lookup
1549 : * @param uuid the UUID that holds the lock
1550 : * @param quantity how many units should be locked
1551 : * @param expiration_time when should the lock expire
1552 : * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the
1553 : * product is unknown OR if there insufficient stocks remaining
1554 : */
1555 : static enum GNUNET_DB_QueryStatus
1556 0 : postgres_lock_product (void *cls,
1557 : const char *instance_id,
1558 : const char *product_id,
1559 : const struct GNUNET_Uuid *uuid,
1560 : uint64_t quantity,
1561 : struct GNUNET_TIME_Timestamp expiration_time)
1562 : {
1563 0 : struct PostgresClosure *pg = cls;
1564 0 : struct GNUNET_PQ_QueryParam params[] = {
1565 0 : GNUNET_PQ_query_param_string (instance_id),
1566 0 : GNUNET_PQ_query_param_string (product_id),
1567 0 : GNUNET_PQ_query_param_auto_from_type (uuid),
1568 0 : GNUNET_PQ_query_param_uint64 (&quantity),
1569 0 : GNUNET_PQ_query_param_timestamp (&expiration_time),
1570 : GNUNET_PQ_query_param_end
1571 : };
1572 :
1573 0 : check_connection (pg);
1574 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1575 : "lock_product",
1576 : params);
1577 : }
1578 :
1579 :
1580 : /**
1581 : * Release all expired product locks, including
1582 : * those from expired offers -- across all
1583 : * instances.
1584 : *
1585 : * @param cls closure
1586 : */
1587 : static void
1588 0 : postgres_expire_locks (void *cls)
1589 : {
1590 0 : struct PostgresClosure *pg = cls;
1591 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1592 0 : struct GNUNET_PQ_QueryParam params[] = {
1593 0 : GNUNET_PQ_query_param_absolute_time (&now),
1594 : GNUNET_PQ_query_param_end
1595 : };
1596 : enum GNUNET_DB_QueryStatus qs1;
1597 : enum GNUNET_DB_QueryStatus qs2;
1598 : enum GNUNET_DB_QueryStatus qs3;
1599 :
1600 0 : check_connection (pg);
1601 0 : qs1 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1602 : "unlock_products",
1603 : params);
1604 0 : if (qs1 < 0)
1605 : {
1606 0 : GNUNET_break (0);
1607 0 : return;
1608 : }
1609 0 : qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1610 : "unlock_orders",
1611 : params);
1612 0 : if (qs2 < 0)
1613 : {
1614 0 : GNUNET_break (0);
1615 0 : return;
1616 : }
1617 0 : qs3 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1618 : "unlock_contracts",
1619 : params);
1620 0 : if (qs3 < 0)
1621 : {
1622 0 : GNUNET_break (0);
1623 0 : return;
1624 : }
1625 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1626 : "Released %d+%d+%d locks\n",
1627 : qs1,
1628 : qs2,
1629 : qs3);
1630 : }
1631 :
1632 :
1633 : /**
1634 : * Delete information about an order. Note that the transaction must
1635 : * enforce that the order is not awaiting payment anymore.
1636 : *
1637 : * @param cls closure
1638 : * @param instance_id instance to delete order of
1639 : * @param order_id order to delete
1640 : * @param force delete claimed but unpaid orders as well
1641 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
1642 : * if pending payment prevents deletion OR order unknown
1643 : */
1644 : static enum GNUNET_DB_QueryStatus
1645 0 : postgres_delete_order (void *cls,
1646 : const char *instance_id,
1647 : const char *order_id,
1648 : bool force)
1649 : {
1650 0 : struct PostgresClosure *pg = cls;
1651 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1652 0 : struct GNUNET_PQ_QueryParam params[] = {
1653 0 : GNUNET_PQ_query_param_string (instance_id),
1654 0 : GNUNET_PQ_query_param_string (order_id),
1655 0 : GNUNET_PQ_query_param_absolute_time (&now),
1656 0 : GNUNET_PQ_query_param_bool (force),
1657 : GNUNET_PQ_query_param_end
1658 : };
1659 0 : struct GNUNET_PQ_QueryParam params2[] = {
1660 0 : GNUNET_PQ_query_param_string (instance_id),
1661 0 : GNUNET_PQ_query_param_string (order_id),
1662 : GNUNET_PQ_query_param_end
1663 : };
1664 : enum GNUNET_DB_QueryStatus qs;
1665 :
1666 0 : check_connection (pg);
1667 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1668 : "delete_order",
1669 : params);
1670 0 : if ( (qs <= 0) || (! force))
1671 0 : return qs;
1672 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1673 : "delete_contract",
1674 : params2);
1675 : }
1676 :
1677 :
1678 : /**
1679 : * Retrieve order given its @a order_id and the @a instance_id.
1680 : *
1681 : * @param cls closure
1682 : * @param instance_id instance to obtain order of
1683 : * @param order_id order id used to perform the lookup
1684 : * @param[out] claim_token the claim token generated for the order,
1685 : * NULL to only test if the order exists
1686 : * @param[out] h_post_data set to the hash of the POST data that created the order
1687 : * @param[out] contract_terms where to store the retrieved contract terms,
1688 : * NULL to only test if the order exists
1689 : * @return transaction status
1690 : */
1691 : static enum GNUNET_DB_QueryStatus
1692 0 : postgres_lookup_order (void *cls,
1693 : const char *instance_id,
1694 : const char *order_id,
1695 : struct TALER_ClaimTokenP *claim_token,
1696 : struct TALER_MerchantPostDataHashP *h_post_data,
1697 : json_t **contract_terms)
1698 : {
1699 0 : struct PostgresClosure *pg = cls;
1700 : json_t *j;
1701 : struct TALER_ClaimTokenP ct;
1702 : enum GNUNET_DB_QueryStatus qs;
1703 0 : struct GNUNET_PQ_QueryParam params[] = {
1704 0 : GNUNET_PQ_query_param_string (instance_id),
1705 0 : GNUNET_PQ_query_param_string (order_id),
1706 : GNUNET_PQ_query_param_end
1707 : };
1708 0 : struct GNUNET_PQ_ResultSpec rs[] = {
1709 0 : TALER_PQ_result_spec_json ("contract_terms",
1710 : &j),
1711 0 : GNUNET_PQ_result_spec_auto_from_type ("claim_token",
1712 : &ct),
1713 0 : GNUNET_PQ_result_spec_auto_from_type ("h_post_data",
1714 : h_post_data),
1715 : GNUNET_PQ_result_spec_end
1716 : };
1717 :
1718 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1719 : "Finding contract term, order_id: '%s', instance_id: '%s'.\n",
1720 : order_id,
1721 : instance_id);
1722 0 : check_connection (pg);
1723 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1724 : "lookup_order",
1725 : params,
1726 : rs);
1727 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
1728 : {
1729 0 : if (NULL != contract_terms)
1730 0 : *contract_terms = j;
1731 : else
1732 0 : json_decref (j);
1733 0 : if (NULL != claim_token)
1734 0 : *claim_token = ct;
1735 : }
1736 : else
1737 : {
1738 : /* just to be safe: NULL it */
1739 0 : if (NULL != contract_terms)
1740 0 : *contract_terms = NULL;
1741 0 : if (NULL != claim_token)
1742 0 : *claim_token = (struct TALER_ClaimTokenP) { 0 }
1743 : ;
1744 : }
1745 0 : return qs;
1746 : }
1747 :
1748 :
1749 : /**
1750 : * Retrieve order summary given its @a order_id and the @a instance_id.
1751 : *
1752 : * @param cls closure
1753 : * @param instance_id instance to obtain order of
1754 : * @param order_id order id used to perform the lookup
1755 : * @param[out] timestamp when was the order created
1756 : * @param[out] order_serial under which serial do we keep this order
1757 : * @return transaction status
1758 : */
1759 : static enum GNUNET_DB_QueryStatus
1760 0 : postgres_lookup_order_summary (void *cls,
1761 : const char *instance_id,
1762 : const char *order_id,
1763 : struct GNUNET_TIME_Timestamp *timestamp,
1764 : uint64_t *order_serial)
1765 : {
1766 0 : struct PostgresClosure *pg = cls;
1767 0 : struct GNUNET_PQ_QueryParam params[] = {
1768 0 : GNUNET_PQ_query_param_string (instance_id),
1769 0 : GNUNET_PQ_query_param_string (order_id),
1770 : GNUNET_PQ_query_param_end
1771 : };
1772 0 : struct GNUNET_PQ_ResultSpec rs[] = {
1773 0 : GNUNET_PQ_result_spec_uint64 ("order_serial",
1774 : order_serial),
1775 0 : GNUNET_PQ_result_spec_timestamp ("creation_time",
1776 : timestamp),
1777 : GNUNET_PQ_result_spec_end
1778 : };
1779 :
1780 0 : check_connection (pg);
1781 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1782 : "lookup_order_summary",
1783 : params,
1784 : rs);
1785 : }
1786 :
1787 :
1788 : /**
1789 : * Context used for postgres_lookup_orders().
1790 : */
1791 : struct LookupOrdersContext
1792 : {
1793 : /**
1794 : * Function to call with the results.
1795 : */
1796 : TALER_MERCHANTDB_OrdersCallback cb;
1797 :
1798 : /**
1799 : * Closure for @a cb.
1800 : */
1801 : void *cb_cls;
1802 :
1803 : /**
1804 : * Did database result extraction fail?
1805 : */
1806 : bool extract_failed;
1807 : };
1808 :
1809 :
1810 : /**
1811 : * Function to be called with the results of a SELECT statement
1812 : * that has returned @a num_results results about orders.
1813 : *
1814 : * @param[in,out] cls of type `struct LookupOrdersContext *`
1815 : * @param result the postgres result
1816 : * @param num_results the number of results in @a result
1817 : */
1818 : static void
1819 0 : lookup_orders_cb (void *cls,
1820 : PGresult *result,
1821 : unsigned int num_results)
1822 : {
1823 0 : struct LookupOrdersContext *plc = cls;
1824 :
1825 0 : for (unsigned int i = 0; i < num_results; i++)
1826 : {
1827 : char *order_id;
1828 : uint64_t order_serial;
1829 : struct GNUNET_TIME_Timestamp ts;
1830 0 : struct GNUNET_PQ_ResultSpec rs[] = {
1831 0 : GNUNET_PQ_result_spec_string ("order_id",
1832 : &order_id),
1833 0 : GNUNET_PQ_result_spec_uint64 ("order_serial",
1834 : &order_serial),
1835 0 : GNUNET_PQ_result_spec_timestamp ("creation_time",
1836 : &ts),
1837 : GNUNET_PQ_result_spec_end
1838 : };
1839 :
1840 0 : if (GNUNET_OK !=
1841 0 : GNUNET_PQ_extract_result (result,
1842 : rs,
1843 : i))
1844 : {
1845 0 : GNUNET_break (0);
1846 0 : plc->extract_failed = true;
1847 0 : return;
1848 : }
1849 0 : plc->cb (plc->cb_cls,
1850 : order_id,
1851 : order_serial,
1852 : ts);
1853 0 : GNUNET_PQ_cleanup_result (rs);
1854 : }
1855 : }
1856 :
1857 :
1858 : /**
1859 : * Retrieve orders given the @a instance_id.
1860 : *
1861 : * @param cls closure
1862 : * @param instance_id instance to obtain order of
1863 : * @param of filter to apply when looking up orders
1864 : * @param cb callback to pass all the orders that are found
1865 : * @param cb_cls closure for @a cb
1866 : * @return transaction status
1867 : */
1868 : static enum GNUNET_DB_QueryStatus
1869 0 : postgres_lookup_orders (void *cls,
1870 : const char *instance_id,
1871 : const struct TALER_MERCHANTDB_OrderFilter *of,
1872 : TALER_MERCHANTDB_OrdersCallback cb,
1873 : void *cb_cls)
1874 : {
1875 0 : struct PostgresClosure *pg = cls;
1876 0 : struct LookupOrdersContext plc = {
1877 : .cb = cb,
1878 : .cb_cls = cb_cls
1879 : };
1880 0 : uint64_t limit = (of->delta > 0) ? of->delta : -of->delta;
1881 : uint8_t paid;
1882 : uint8_t refunded;
1883 : uint8_t wired;
1884 0 : struct GNUNET_PQ_QueryParam params[] = {
1885 0 : GNUNET_PQ_query_param_string (instance_id),
1886 0 : GNUNET_PQ_query_param_uint64 (&limit),
1887 0 : GNUNET_PQ_query_param_uint64 (&of->start_row),
1888 0 : GNUNET_PQ_query_param_timestamp (&of->date),
1889 0 : GNUNET_PQ_query_param_auto_from_type (&paid),
1890 0 : GNUNET_PQ_query_param_auto_from_type (&refunded),
1891 0 : GNUNET_PQ_query_param_auto_from_type (&wired),
1892 : GNUNET_PQ_query_param_end
1893 : };
1894 : enum GNUNET_DB_QueryStatus qs;
1895 : char stmt[128];
1896 :
1897 0 : paid = (TALER_EXCHANGE_YNA_YES == of->paid);
1898 0 : refunded = (TALER_EXCHANGE_YNA_YES == of->refunded);
1899 0 : wired = (TALER_EXCHANGE_YNA_YES == of->wired);
1900 : /* painfully many cases..., note that "_xxx" being present in 'stmt' merely
1901 : means that we filter by that variable, the value we filter for is
1902 : computed above */
1903 0 : GNUNET_snprintf (stmt,
1904 : sizeof (stmt),
1905 : "lookup_orders_%s%s%s%s",
1906 0 : (of->delta > 0) ? "inc" : "dec",
1907 0 : (TALER_EXCHANGE_YNA_ALL == of->paid) ? "" : "_paid",
1908 0 : (TALER_EXCHANGE_YNA_ALL == of->refunded) ? "" :
1909 : "_refunded",
1910 0 : (TALER_EXCHANGE_YNA_ALL == of->wired) ? "" : "_wired");
1911 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
1912 : stmt,
1913 : params,
1914 : &lookup_orders_cb,
1915 : &plc);
1916 0 : if (plc.extract_failed)
1917 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1918 0 : return qs;
1919 : }
1920 :
1921 :
1922 : /**
1923 : * Insert order into the DB.
1924 : *
1925 : * @param cls closure
1926 : * @param instance_id identifies the instance responsible for the order
1927 : * @param order_id alphanumeric string that uniquely identifies the proposal
1928 : * @param h_post_data hash of the POST data for idempotency checks
1929 : * @param pay_deadline how long does the customer have to pay for the order
1930 : * @param claim_token token to use for access control
1931 : * @param contract_terms proposal data to store
1932 : * @return transaction status
1933 : */
1934 : static enum GNUNET_DB_QueryStatus
1935 0 : postgres_insert_order (void *cls,
1936 : const char *instance_id,
1937 : const char *order_id,
1938 : const struct TALER_MerchantPostDataHashP *h_post_data,
1939 : struct GNUNET_TIME_Timestamp pay_deadline,
1940 : const struct TALER_ClaimTokenP *claim_token,
1941 : const json_t *contract_terms)
1942 : {
1943 0 : struct PostgresClosure *pg = cls;
1944 : struct GNUNET_TIME_Timestamp now;
1945 0 : struct GNUNET_PQ_QueryParam params[] = {
1946 0 : GNUNET_PQ_query_param_string (instance_id),
1947 0 : GNUNET_PQ_query_param_string (order_id),
1948 0 : GNUNET_PQ_query_param_timestamp (&pay_deadline),
1949 0 : GNUNET_PQ_query_param_auto_from_type (claim_token),
1950 0 : GNUNET_PQ_query_param_auto_from_type (h_post_data),
1951 0 : GNUNET_PQ_query_param_timestamp (&now),
1952 0 : TALER_PQ_query_param_json (contract_terms),
1953 : GNUNET_PQ_query_param_end
1954 : };
1955 :
1956 0 : now = GNUNET_TIME_timestamp_get ();
1957 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1958 : "inserting order: order_id: %s, instance_id: %s.\n",
1959 : order_id,
1960 : instance_id);
1961 0 : check_connection (pg);
1962 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1963 : "insert_order",
1964 : params);
1965 : }
1966 :
1967 :
1968 : /**
1969 : * Release an inventory lock by UUID. Releases ALL stocks locked under
1970 : * the given UUID.
1971 : *
1972 : * @param cls closure
1973 : * @param uuid the UUID to release locks for
1974 : * @return transaction status,
1975 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are no locks under @a uuid
1976 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success
1977 : */
1978 : static enum GNUNET_DB_QueryStatus
1979 0 : postgres_unlock_inventory (void *cls,
1980 : const struct GNUNET_Uuid *uuid)
1981 : {
1982 0 : struct PostgresClosure *pg = cls;
1983 0 : struct GNUNET_PQ_QueryParam params[] = {
1984 0 : GNUNET_PQ_query_param_auto_from_type (uuid),
1985 : GNUNET_PQ_query_param_end
1986 : };
1987 :
1988 0 : check_connection (pg);
1989 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1990 : "unlock_inventory",
1991 : params);
1992 : }
1993 :
1994 :
1995 : /**
1996 : * Lock inventory stock to a particular order.
1997 : *
1998 : * @param cls closure
1999 : * @param instance_id identifies the instance responsible for the order
2000 : * @param order_id alphanumeric string that uniquely identifies the order
2001 : * @param product_id uniquely identifies the product to be locked
2002 : * @param quantity how many units should be locked to the @a order_id
2003 : * @return transaction status,
2004 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are insufficient stocks
2005 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success
2006 : */
2007 : static enum GNUNET_DB_QueryStatus
2008 0 : postgres_insert_order_lock (void *cls,
2009 : const char *instance_id,
2010 : const char *order_id,
2011 : const char *product_id,
2012 : uint64_t quantity)
2013 : {
2014 0 : struct PostgresClosure *pg = cls;
2015 0 : struct GNUNET_PQ_QueryParam params[] = {
2016 0 : GNUNET_PQ_query_param_string (instance_id),
2017 0 : GNUNET_PQ_query_param_string (order_id),
2018 0 : GNUNET_PQ_query_param_string (product_id),
2019 0 : GNUNET_PQ_query_param_uint64 (&quantity),
2020 : GNUNET_PQ_query_param_end
2021 : };
2022 :
2023 0 : check_connection (pg);
2024 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2025 : "insert_order_lock",
2026 : params);
2027 : }
2028 :
2029 :
2030 : /**
2031 : * Retrieve contract terms given its @a order_id
2032 : *
2033 : * @param cls closure
2034 : * @param instance_id instance's identifier
2035 : * @param order_id order_id used to lookup.
2036 : * @param[out] contract_terms where to store the result, NULL to only check for existence
2037 : * @param[out] order_serial set to the order's serial number
2038 : * @param[out] paid set to true if the order is fully paid
2039 : * @param[out] claim_token set to the claim token, NULL to only check for existence
2040 : * @return transaction status
2041 : */
2042 : static enum GNUNET_DB_QueryStatus
2043 0 : postgres_lookup_contract_terms (void *cls,
2044 : const char *instance_id,
2045 : const char *order_id,
2046 : json_t **contract_terms,
2047 : uint64_t *order_serial,
2048 : bool *paid,
2049 : struct TALER_ClaimTokenP *claim_token)
2050 : {
2051 0 : struct PostgresClosure *pg = cls;
2052 : enum GNUNET_DB_QueryStatus qs;
2053 : struct TALER_ClaimTokenP ct;
2054 0 : struct GNUNET_PQ_QueryParam params[] = {
2055 0 : GNUNET_PQ_query_param_string (instance_id),
2056 0 : GNUNET_PQ_query_param_string (order_id),
2057 : GNUNET_PQ_query_param_end
2058 : };
2059 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2060 : /* contract_terms must be first! */
2061 0 : TALER_PQ_result_spec_json ("contract_terms",
2062 : contract_terms),
2063 0 : GNUNET_PQ_result_spec_uint64 ("order_serial",
2064 : order_serial),
2065 0 : GNUNET_PQ_result_spec_bool ("paid",
2066 : paid),
2067 0 : GNUNET_PQ_result_spec_auto_from_type ("claim_token",
2068 : &ct),
2069 : GNUNET_PQ_result_spec_end
2070 : };
2071 :
2072 0 : check_connection (pg);
2073 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2074 : "lookup_contract_terms",
2075 : params,
2076 : (NULL != contract_terms)
2077 : ? rs
2078 : : &rs[1]);
2079 0 : if (NULL != claim_token)
2080 0 : *claim_token = ct;
2081 0 : return qs;
2082 : }
2083 :
2084 :
2085 : /**
2086 : * Store contract terms given its @a order_id. Note that some attributes are
2087 : * expected to be calculated inside of the function, like the hash of the
2088 : * contract terms (to be hashed), the creation_time and pay_deadline (to be
2089 : * obtained from the merchant_orders table). The "session_id" should be
2090 : * initially set to the empty string. The "fulfillment_url" and "refund_deadline"
2091 : * must be extracted from @a contract_terms.
2092 : *
2093 : * @param cls closure
2094 : * @param instance_id instance's identifier
2095 : * @param order_id order_id used to store
2096 : * @param contract_terms contract terms to store
2097 : * @param[out] order_serial set to the serial of the order
2098 : * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms
2099 : * is malformed
2100 : */
2101 : static enum GNUNET_DB_QueryStatus
2102 0 : postgres_insert_contract_terms (void *cls,
2103 : const char *instance_id,
2104 : const char *order_id,
2105 : json_t *contract_terms,
2106 : uint64_t *order_serial)
2107 : {
2108 0 : struct PostgresClosure *pg = cls;
2109 : struct GNUNET_TIME_Timestamp pay_deadline;
2110 : struct GNUNET_TIME_Timestamp refund_deadline;
2111 : const char *fulfillment_url;
2112 : struct TALER_PrivateContractHashP h_contract_terms;
2113 :
2114 0 : if (GNUNET_OK !=
2115 0 : TALER_JSON_contract_hash (contract_terms,
2116 : &h_contract_terms))
2117 : {
2118 0 : GNUNET_break (0);
2119 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2120 : }
2121 :
2122 : {
2123 : struct GNUNET_JSON_Specification spec[] = {
2124 0 : GNUNET_JSON_spec_timestamp ("pay_deadline",
2125 : &pay_deadline),
2126 0 : GNUNET_JSON_spec_timestamp ("refund_deadline",
2127 : &refund_deadline),
2128 0 : GNUNET_JSON_spec_end ()
2129 : };
2130 : enum GNUNET_GenericReturnValue res;
2131 :
2132 0 : res = TALER_MHD_parse_json_data (NULL,
2133 : contract_terms,
2134 : spec);
2135 0 : if (GNUNET_OK != res)
2136 : {
2137 0 : GNUNET_break (0);
2138 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2139 : }
2140 : }
2141 :
2142 : fulfillment_url =
2143 0 : json_string_value (json_object_get (contract_terms,
2144 : "fulfillment_url"));
2145 0 : check_connection (pg);
2146 : {
2147 0 : struct GNUNET_PQ_QueryParam params[] = {
2148 0 : GNUNET_PQ_query_param_string (instance_id),
2149 0 : GNUNET_PQ_query_param_string (order_id),
2150 0 : TALER_PQ_query_param_json (contract_terms),
2151 0 : GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
2152 0 : GNUNET_PQ_query_param_timestamp (&pay_deadline),
2153 0 : GNUNET_PQ_query_param_timestamp (&refund_deadline),
2154 : (NULL == fulfillment_url)
2155 0 : ? GNUNET_PQ_query_param_null ()
2156 0 : : GNUNET_PQ_query_param_string (fulfillment_url),
2157 : GNUNET_PQ_query_param_end
2158 : };
2159 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2160 0 : GNUNET_PQ_result_spec_uint64 ("order_serial",
2161 : order_serial),
2162 : GNUNET_PQ_result_spec_end
2163 : };
2164 :
2165 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2166 : "insert_contract_terms",
2167 : params,
2168 : rs);
2169 : }
2170 : }
2171 :
2172 :
2173 : /**
2174 : * Update the contract terms stored for @a order_id. Note that some attributes are
2175 : * expected to be calculated inside of the function, like the hash of the
2176 : * contract terms (to be hashed), the creation_time and pay_deadline (to be
2177 : * obtained from the merchant_orders table). The "session_id" should be
2178 : * initially set to the empty string. The "fulfillment_url" and "refund_deadline"
2179 : * must be extracted from @a contract_terms.
2180 : *
2181 : * @param cls closure
2182 : * @param instance_id instance's identifier
2183 : * @param order_id order_id used to store
2184 : * @param contract_terms contract to store
2185 : * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms
2186 : * is malformed
2187 : */
2188 : static enum GNUNET_DB_QueryStatus
2189 0 : postgres_update_contract_terms (void *cls,
2190 : const char *instance_id,
2191 : const char *order_id,
2192 : json_t *contract_terms)
2193 : {
2194 0 : struct PostgresClosure *pg = cls;
2195 : struct GNUNET_TIME_Timestamp pay_deadline;
2196 : struct GNUNET_TIME_Timestamp refund_deadline;
2197 0 : const char *fulfillment_url = NULL;
2198 : struct TALER_PrivateContractHashP h_contract_terms;
2199 :
2200 0 : if (GNUNET_OK !=
2201 0 : TALER_JSON_contract_hash (contract_terms,
2202 : &h_contract_terms))
2203 : {
2204 0 : GNUNET_break (0);
2205 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2206 : }
2207 :
2208 : {
2209 : struct GNUNET_JSON_Specification spec[] = {
2210 0 : GNUNET_JSON_spec_timestamp ("pay_deadline",
2211 : &pay_deadline),
2212 0 : GNUNET_JSON_spec_timestamp ("refund_deadline",
2213 : &refund_deadline),
2214 0 : GNUNET_JSON_spec_mark_optional (
2215 : GNUNET_JSON_spec_string ("fulfillment_url",
2216 : &fulfillment_url),
2217 : NULL),
2218 0 : GNUNET_JSON_spec_end ()
2219 : };
2220 : enum GNUNET_GenericReturnValue res;
2221 :
2222 0 : res = TALER_MHD_parse_json_data (NULL,
2223 : contract_terms,
2224 : spec);
2225 0 : if (GNUNET_OK != res)
2226 : {
2227 0 : GNUNET_break (0);
2228 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2229 : }
2230 : }
2231 :
2232 0 : check_connection (pg);
2233 : {
2234 0 : struct GNUNET_PQ_QueryParam params[] = {
2235 0 : GNUNET_PQ_query_param_string (instance_id),
2236 0 : GNUNET_PQ_query_param_string (order_id),
2237 0 : TALER_PQ_query_param_json (contract_terms),
2238 0 : GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
2239 0 : GNUNET_PQ_query_param_timestamp (&pay_deadline),
2240 0 : GNUNET_PQ_query_param_timestamp (&refund_deadline),
2241 0 : (NULL == fulfillment_url)
2242 0 : ? GNUNET_PQ_query_param_null ()
2243 0 : : GNUNET_PQ_query_param_string (fulfillment_url),
2244 : GNUNET_PQ_query_param_end
2245 : };
2246 :
2247 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2248 : "update_contract_terms",
2249 : params);
2250 : }
2251 : }
2252 :
2253 :
2254 : /**
2255 : * Delete information about a contract. Note that the transaction must
2256 : * enforce that the contract is not awaiting payment anymore AND was not
2257 : * paid, or is past the legal expiration.
2258 : *
2259 : * @param cls closure
2260 : * @param instance_id instance to delete order of
2261 : * @param order_id order to delete
2262 : * @param legal_expiration how long do we need to keep (paid) contracts on
2263 : * file for legal reasons (i.e. taxation)
2264 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
2265 : * if locks prevent deletion OR order unknown
2266 : */
2267 : static enum GNUNET_DB_QueryStatus
2268 0 : postgres_delete_contract_terms (void *cls,
2269 : const char *instance_id,
2270 : const char *order_id,
2271 : struct GNUNET_TIME_Relative legal_expiration)
2272 : {
2273 0 : struct PostgresClosure *pg = cls;
2274 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
2275 0 : struct GNUNET_PQ_QueryParam params[] = {
2276 0 : GNUNET_PQ_query_param_string (instance_id),
2277 0 : GNUNET_PQ_query_param_string (order_id),
2278 0 : GNUNET_PQ_query_param_relative_time (&legal_expiration),
2279 0 : GNUNET_PQ_query_param_absolute_time (&now),
2280 : GNUNET_PQ_query_param_end
2281 : };
2282 :
2283 0 : check_connection (pg);
2284 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2285 : "delete_contract_terms",
2286 : params);
2287 : }
2288 :
2289 :
2290 : /**
2291 : * Closure for #lookup_deposits_cb().
2292 : */
2293 : struct LookupDepositsContext
2294 : {
2295 : /**
2296 : * Function to call with results.
2297 : */
2298 : TALER_MERCHANTDB_DepositsCallback cb;
2299 :
2300 : /**
2301 : * Closure for @e cls.
2302 : */
2303 : void *cb_cls;
2304 :
2305 : /**
2306 : * Plugin context.
2307 : */
2308 : struct PostgresClosure *pg;
2309 :
2310 : /**
2311 : * Transaction status (set).
2312 : */
2313 : enum GNUNET_DB_QueryStatus qs;
2314 : };
2315 :
2316 :
2317 : /**
2318 : * Function to be called with the results of a SELECT statement
2319 : * that has returned @a num_results results.
2320 : *
2321 : * @param[in,out] cls of type `struct LookupDepositsContext *`
2322 : * @param result the postgres result
2323 : * @param num_results the number of results in @a result
2324 : */
2325 : static void
2326 0 : lookup_deposits_cb (void *cls,
2327 : PGresult *result,
2328 : unsigned int num_results)
2329 : {
2330 0 : struct LookupDepositsContext *ldc = cls;
2331 0 : struct PostgresClosure *pg = ldc->pg;
2332 :
2333 0 : for (unsigned int i = 0; i<num_results; i++)
2334 : {
2335 : struct TALER_CoinSpendPublicKeyP coin_pub;
2336 : struct TALER_Amount amount_with_fee;
2337 : struct TALER_Amount deposit_fee;
2338 : struct TALER_Amount refund_fee;
2339 : struct TALER_Amount wire_fee;
2340 : char *exchange_url;
2341 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2342 0 : GNUNET_PQ_result_spec_string ("exchange_url",
2343 : &exchange_url),
2344 0 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
2345 : &coin_pub),
2346 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
2347 : &amount_with_fee),
2348 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
2349 : &deposit_fee),
2350 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
2351 : &refund_fee),
2352 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
2353 : &wire_fee),
2354 : GNUNET_PQ_result_spec_end
2355 : };
2356 :
2357 0 : if (GNUNET_OK !=
2358 0 : GNUNET_PQ_extract_result (result,
2359 : rs,
2360 : i))
2361 : {
2362 0 : GNUNET_break (0);
2363 0 : ldc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2364 0 : return;
2365 : }
2366 0 : ldc->cb (ldc->cb_cls,
2367 : exchange_url,
2368 : &coin_pub,
2369 : &amount_with_fee,
2370 : &deposit_fee,
2371 : &refund_fee,
2372 : &wire_fee);
2373 0 : GNUNET_PQ_cleanup_result (rs);
2374 : }
2375 0 : ldc->qs = num_results;
2376 : }
2377 :
2378 :
2379 : /**
2380 : * Lookup information about coins that were successfully deposited for a
2381 : * given contract.
2382 : *
2383 : * @param cls closure
2384 : * @param instance_id instance to lookup deposits for
2385 : * @param h_contract_terms proposal data's hashcode
2386 : * @param cb function to call with payment data
2387 : * @param cb_cls closure for @a cb
2388 : * @return transaction status
2389 : */
2390 : static enum GNUNET_DB_QueryStatus
2391 0 : postgres_lookup_deposits (
2392 : void *cls,
2393 : const char *instance_id,
2394 : const struct TALER_PrivateContractHashP *h_contract_terms,
2395 : TALER_MERCHANTDB_DepositsCallback cb,
2396 : void *cb_cls)
2397 : {
2398 0 : struct PostgresClosure *pg = cls;
2399 0 : struct GNUNET_PQ_QueryParam params[] = {
2400 0 : GNUNET_PQ_query_param_string (instance_id),
2401 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2402 : GNUNET_PQ_query_param_end
2403 : };
2404 0 : struct LookupDepositsContext ldc = {
2405 : .cb = cb,
2406 : .cb_cls = cb_cls,
2407 : .pg = pg
2408 : };
2409 : enum GNUNET_DB_QueryStatus qs;
2410 :
2411 : /* no preflight check here, run in its own transaction by the caller! */
2412 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2413 : "Finding deposits for h_contract_terms '%s'\n",
2414 : GNUNET_h2s (&h_contract_terms->hash));
2415 0 : check_connection (pg);
2416 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
2417 : "lookup_deposits",
2418 : params,
2419 : &lookup_deposits_cb,
2420 : &ldc);
2421 0 : if (qs <= 0)
2422 0 : return qs;
2423 0 : return ldc.qs;
2424 : }
2425 :
2426 :
2427 : /**
2428 : * Insert an exchange signing key into our database.
2429 : *
2430 : * @param cls closure
2431 : * @param master_pub exchange master public key used for @a master_sig
2432 : * @param exchange_pub exchange signing key to insert
2433 : * @param start_date when does the signing key become valid
2434 : * @param expire_date when does the signing key stop being used
2435 : * @param end_date when does the signing key become void as proof
2436 : * @param master_sig signature of @a master_pub over the @a exchange_pub and the dates
2437 : */
2438 : static enum GNUNET_DB_QueryStatus
2439 0 : postgres_insert_exchange_signkey (
2440 : void *cls,
2441 : const struct TALER_MasterPublicKeyP *master_pub,
2442 : const struct TALER_ExchangePublicKeyP *exchange_pub,
2443 : struct GNUNET_TIME_Timestamp start_date,
2444 : struct GNUNET_TIME_Timestamp expire_date,
2445 : struct GNUNET_TIME_Timestamp end_date,
2446 : const struct TALER_MasterSignatureP *master_sig)
2447 : {
2448 0 : struct PostgresClosure *pg = cls;
2449 0 : struct GNUNET_PQ_QueryParam params[] = {
2450 0 : GNUNET_PQ_query_param_auto_from_type (master_pub),
2451 0 : GNUNET_PQ_query_param_auto_from_type (exchange_pub),
2452 0 : GNUNET_PQ_query_param_timestamp (&start_date),
2453 0 : GNUNET_PQ_query_param_timestamp (&expire_date),
2454 0 : GNUNET_PQ_query_param_timestamp (&end_date),
2455 0 : GNUNET_PQ_query_param_auto_from_type (master_sig),
2456 : GNUNET_PQ_query_param_end
2457 : };
2458 :
2459 0 : check_connection (pg);
2460 0 : postgres_preflight (pg);
2461 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2462 : "insert_exchange_signkey",
2463 : params);
2464 :
2465 : }
2466 :
2467 :
2468 : /**
2469 : * Insert payment confirmation from the exchange into the database.
2470 : *
2471 : * @param cls closure
2472 : * @param instance_id instance to lookup deposits for
2473 : * @param deposit_timestamp time when the exchange generated the deposit confirmation
2474 : * @param h_contract_terms proposal data's hashcode
2475 : * @param coin_pub public key of the coin
2476 : * @param exchange_url URL of the exchange that issued @a coin_pub
2477 : * @param amount_with_fee amount the exchange will deposit for this coin
2478 : * @param deposit_fee fee the exchange will charge for this coin
2479 : * @param wire_fee wire fee the exchange charges
2480 : * @param refund_fee fee the exchange charges to refund this coin
2481 : * @param h_wire hash of the wire details of the target account of the merchant
2482 : * @param exchange_sig signature from exchange that coin was accepted
2483 : * @param exchange_pub signgin key that was used for @a exchange_sig
2484 : * @return transaction status
2485 : */
2486 : static enum GNUNET_DB_QueryStatus
2487 0 : postgres_insert_deposit (
2488 : void *cls,
2489 : const char *instance_id,
2490 : struct GNUNET_TIME_Timestamp deposit_timestamp,
2491 : const struct TALER_PrivateContractHashP *h_contract_terms,
2492 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2493 : const char *exchange_url,
2494 : const struct TALER_Amount *amount_with_fee,
2495 : const struct TALER_Amount *deposit_fee,
2496 : const struct TALER_Amount *refund_fee,
2497 : const struct TALER_Amount *wire_fee,
2498 : const struct TALER_MerchantWireHashP *h_wire,
2499 : const struct TALER_ExchangeSignatureP *exchange_sig,
2500 : const struct TALER_ExchangePublicKeyP *exchange_pub)
2501 : {
2502 0 : struct PostgresClosure *pg = cls;
2503 0 : struct GNUNET_PQ_QueryParam params[] = {
2504 0 : GNUNET_PQ_query_param_string (instance_id),
2505 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2506 0 : GNUNET_PQ_query_param_timestamp (&deposit_timestamp), /* $3 */
2507 0 : GNUNET_PQ_query_param_auto_from_type (coin_pub),
2508 0 : GNUNET_PQ_query_param_string (exchange_url),
2509 0 : TALER_PQ_query_param_amount (amount_with_fee), /* $6/$7 */
2510 0 : TALER_PQ_query_param_amount (deposit_fee), /* $8, $9 */
2511 0 : TALER_PQ_query_param_amount (refund_fee), /* $10, $11 */
2512 0 : TALER_PQ_query_param_amount (wire_fee), /* $12, $13 */
2513 0 : GNUNET_PQ_query_param_auto_from_type (h_wire), /* $14 */
2514 0 : GNUNET_PQ_query_param_auto_from_type (exchange_sig), /* $15 */
2515 0 : GNUNET_PQ_query_param_auto_from_type (exchange_pub), /* $16 */
2516 : GNUNET_PQ_query_param_end
2517 : };
2518 :
2519 : /* no preflight check here, run in transaction by caller! */
2520 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2521 : "Storing deposit for instance `%s' h_contract_terms `%s', coin_pub: `%s', amount_with_fee: %s\n",
2522 : instance_id,
2523 : GNUNET_h2s (&h_contract_terms->hash),
2524 : TALER_B2S (coin_pub),
2525 : TALER_amount2s (amount_with_fee));
2526 0 : check_connection (pg);
2527 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2528 : "insert_deposit",
2529 : params);
2530 :
2531 : }
2532 :
2533 :
2534 : /**
2535 : * Closure for #lookup_refunds_cb().
2536 : */
2537 : struct LookupRefundsContext
2538 : {
2539 : /**
2540 : * Function to call for each refund.
2541 : */
2542 : TALER_MERCHANTDB_RefundCallback rc;
2543 :
2544 : /**
2545 : * Closure for @e rc.
2546 : */
2547 : void *rc_cls;
2548 :
2549 : /**
2550 : * Plugin context.
2551 : */
2552 : struct PostgresClosure *pg;
2553 :
2554 : /**
2555 : * Transaction result.
2556 : */
2557 : enum GNUNET_DB_QueryStatus qs;
2558 : };
2559 :
2560 :
2561 : /**
2562 : * Function to be called with the results of a SELECT statement
2563 : * that has returned @a num_results results.
2564 : *
2565 : * @param cls of type `struct LookupRefundsContext *`
2566 : * @param result the postgres result
2567 : * @param num_results the number of results in @a result
2568 : */
2569 : static void
2570 0 : lookup_refunds_cb (void *cls,
2571 : PGresult *result,
2572 : unsigned int num_results)
2573 : {
2574 0 : struct LookupRefundsContext *lrc = cls;
2575 0 : struct PostgresClosure *pg = lrc->pg;
2576 :
2577 0 : for (unsigned int i = 0; i<num_results; i++)
2578 : {
2579 : struct TALER_CoinSpendPublicKeyP coin_pub;
2580 : struct TALER_Amount refund_amount;
2581 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2582 0 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
2583 : &coin_pub),
2584 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
2585 : &refund_amount),
2586 : GNUNET_PQ_result_spec_end
2587 : };
2588 :
2589 0 : if (GNUNET_OK !=
2590 0 : GNUNET_PQ_extract_result (result,
2591 : rs,
2592 : i))
2593 : {
2594 0 : GNUNET_break (0);
2595 0 : lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2596 0 : return;
2597 : }
2598 0 : lrc->rc (lrc->rc_cls,
2599 : &coin_pub,
2600 : &refund_amount);
2601 0 : GNUNET_PQ_cleanup_result (rs); /* technically useless here */
2602 : }
2603 0 : lrc->qs = num_results;
2604 : }
2605 :
2606 :
2607 : /**
2608 : * Obtain refunds associated with a contract.
2609 : *
2610 : * @param cls closure, typically a connection to the db
2611 : * @param instance_id instance to lookup refunds for
2612 : * @param h_contract_terms hash code of the contract
2613 : * @param rc function to call for each coin on which there is a refund
2614 : * @param rc_cls closure for @a rc
2615 : * @return transaction status
2616 : */
2617 : static enum GNUNET_DB_QueryStatus
2618 0 : postgres_lookup_refunds (
2619 : void *cls,
2620 : const char *instance_id,
2621 : const struct TALER_PrivateContractHashP *h_contract_terms,
2622 : TALER_MERCHANTDB_RefundCallback rc,
2623 : void *rc_cls)
2624 : {
2625 0 : struct PostgresClosure *pg = cls;
2626 0 : struct GNUNET_PQ_QueryParam params[] = {
2627 0 : GNUNET_PQ_query_param_string (instance_id),
2628 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2629 : GNUNET_PQ_query_param_end
2630 : };
2631 0 : struct LookupRefundsContext lrc = {
2632 : .rc = rc,
2633 : .rc_cls = rc_cls,
2634 : .pg = pg
2635 : };
2636 : enum GNUNET_DB_QueryStatus qs;
2637 :
2638 : /* no preflight check here, run in transaction by caller! */
2639 0 : TALER_LOG_DEBUG ("Looking for refund of h_contract_terms %s at `%s'\n",
2640 : GNUNET_h2s (&h_contract_terms->hash),
2641 : instance_id);
2642 0 : check_connection (pg);
2643 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
2644 : "lookup_refunds",
2645 : params,
2646 : &lookup_refunds_cb,
2647 : &lrc);
2648 0 : if (0 >= qs)
2649 0 : return qs;
2650 0 : return lrc.qs;
2651 : }
2652 :
2653 :
2654 : /**
2655 : * Mark contract as paid and store the current @a session_id
2656 : * for which the contract was paid. Deletes the underlying order
2657 : * and marks the locked stocks of the order as sold.
2658 : *
2659 : * @param cls closure
2660 : * @param instance_id instance to mark contract as paid for
2661 : * @param h_contract_terms hash of the contract that is now paid
2662 : * @param session_id the session that paid the contract
2663 : * @return transaction status
2664 : */
2665 : static enum GNUNET_DB_QueryStatus
2666 0 : postgres_mark_contract_paid (
2667 : void *cls,
2668 : const char *instance_id,
2669 : const struct TALER_PrivateContractHashP *h_contract_terms,
2670 : const char *session_id)
2671 : {
2672 0 : struct PostgresClosure *pg = cls;
2673 0 : struct GNUNET_PQ_QueryParam params[] = {
2674 0 : GNUNET_PQ_query_param_string (instance_id),
2675 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2676 0 : GNUNET_PQ_query_param_string (session_id),
2677 : GNUNET_PQ_query_param_end
2678 : };
2679 0 : struct GNUNET_PQ_QueryParam uparams[] = {
2680 0 : GNUNET_PQ_query_param_string (instance_id),
2681 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2682 : GNUNET_PQ_query_param_end
2683 : };
2684 : enum GNUNET_DB_QueryStatus qs;
2685 :
2686 : /* Session ID must always be given by the caller. */
2687 0 : GNUNET_assert (NULL != session_id);
2688 :
2689 : /* no preflight check here, run in transaction by caller! */
2690 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2691 : "Marking h_contract_terms '%s' of %s as paid for session `%s'\n",
2692 : GNUNET_h2s (&h_contract_terms->hash),
2693 : instance_id,
2694 : session_id);
2695 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
2696 : "mark_contract_paid",
2697 : params);
2698 0 : if (qs <= 0)
2699 0 : return qs;
2700 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
2701 : "mark_inventory_sold",
2702 : uparams);
2703 0 : if (qs < 0)
2704 0 : return qs; /* 0: no inventory management, that's OK! */
2705 : /* ON DELETE CASCADE deletes from merchant_order_locks */
2706 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2707 : "delete_completed_order",
2708 : uparams);
2709 : }
2710 :
2711 :
2712 : /**
2713 : * Function called during aborts to refund a coin. Marks the
2714 : * respective coin as refunded.
2715 : *
2716 : * @param cls closure
2717 : * @param instance_id instance to refund payment for
2718 : * @param h_contract_terms hash of the contract to refund coin for
2719 : * @param refund_timestamp timestamp of the refund
2720 : * @param coin_pub public key of the coin to refund (fully)
2721 : * @param reason text justifying the refund
2722 : * @return transaction status
2723 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a coin_pub is unknown to us;
2724 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
2725 : * regardless of whether it actually increased the refund
2726 : */
2727 : static enum GNUNET_DB_QueryStatus
2728 0 : postgres_refund_coin (void *cls,
2729 : const char *instance_id,
2730 : const struct TALER_PrivateContractHashP *h_contract_terms,
2731 : struct GNUNET_TIME_Timestamp refund_timestamp,
2732 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2733 : const char *reason)
2734 : {
2735 0 : struct PostgresClosure *pg = cls;
2736 0 : struct GNUNET_PQ_QueryParam params[] = {
2737 0 : GNUNET_PQ_query_param_string (instance_id),
2738 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2739 0 : GNUNET_PQ_query_param_timestamp (&refund_timestamp),
2740 0 : GNUNET_PQ_query_param_auto_from_type (coin_pub),
2741 0 : GNUNET_PQ_query_param_string (reason),
2742 : GNUNET_PQ_query_param_end
2743 : };
2744 :
2745 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2746 : "refund_coin",
2747 : params);
2748 : }
2749 :
2750 :
2751 : /**
2752 : * Retrieve contract terms given its @a order_id
2753 : *
2754 : * @param cls closure
2755 : * @param instance_id instance's identifier
2756 : * @param order_id order to lookup contract for
2757 : * @param[out] h_contract_terms set to the hash of the contract.
2758 : * @param[out] paid set to the payment status of the contract
2759 : * @return transaction status
2760 : */
2761 : static enum GNUNET_DB_QueryStatus
2762 0 : postgres_lookup_order_status (void *cls,
2763 : const char *instance_id,
2764 : const char *order_id,
2765 : struct TALER_PrivateContractHashP *
2766 : h_contract_terms,
2767 : bool *paid)
2768 : {
2769 0 : struct PostgresClosure *pg = cls;
2770 : uint8_t paid8;
2771 : enum GNUNET_DB_QueryStatus qs;
2772 0 : struct GNUNET_PQ_QueryParam params[] = {
2773 0 : GNUNET_PQ_query_param_string (instance_id),
2774 0 : GNUNET_PQ_query_param_string (order_id),
2775 : GNUNET_PQ_query_param_end
2776 : };
2777 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2778 0 : GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
2779 : h_contract_terms),
2780 0 : GNUNET_PQ_result_spec_auto_from_type ("paid",
2781 : &paid8),
2782 : GNUNET_PQ_result_spec_end
2783 : };
2784 :
2785 0 : check_connection (pg);
2786 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2787 : "lookup_order_status",
2788 : params,
2789 : rs);
2790 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
2791 0 : *paid = (0 != paid8);
2792 : else
2793 0 : *paid = false; /* just to be safe(r) */
2794 0 : return qs;
2795 : }
2796 :
2797 :
2798 : /**
2799 : * Retrieve contract terms given its @a order_serial
2800 : *
2801 : * @param cls closure
2802 : * @param instance_id instance's identifier
2803 : * @param order_serial serial ID of the order to look up
2804 : * @param[out] order_id set to ID of the order
2805 : * @param[out] h_contract_terms set to the hash of the contract.
2806 : * @param[out] paid set to the payment status of the contract
2807 : * @return transaction status
2808 : */
2809 : static enum GNUNET_DB_QueryStatus
2810 0 : postgres_lookup_order_status_by_serial (void *cls,
2811 : const char *instance_id,
2812 : uint64_t order_serial,
2813 : char **order_id,
2814 : struct TALER_PrivateContractHashP *
2815 : h_contract_terms,
2816 : bool *paid)
2817 : {
2818 0 : struct PostgresClosure *pg = cls;
2819 : uint8_t paid8;
2820 : enum GNUNET_DB_QueryStatus qs;
2821 0 : struct GNUNET_PQ_QueryParam params[] = {
2822 0 : GNUNET_PQ_query_param_string (instance_id),
2823 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
2824 : GNUNET_PQ_query_param_end
2825 : };
2826 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2827 0 : GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
2828 : h_contract_terms),
2829 0 : GNUNET_PQ_result_spec_auto_from_type ("paid",
2830 : &paid8),
2831 0 : GNUNET_PQ_result_spec_string ("order_id",
2832 : order_id),
2833 : GNUNET_PQ_result_spec_end
2834 : };
2835 :
2836 0 : check_connection (pg);
2837 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2838 : "lookup_order_status_by_serial",
2839 : params,
2840 : rs);
2841 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
2842 0 : *paid = (0 != paid8);
2843 : else
2844 0 : *paid = false; /* just to be safe(r) */
2845 0 : return qs;
2846 : }
2847 :
2848 :
2849 : /**
2850 : * Retrieve payment and wire status for a given @a order_serial and session ID.
2851 : *
2852 : * @param cls closure
2853 : * @param order_serial identifies the order
2854 : * @param session_id session for which to check the payment status, NULL for any
2855 : * @param[out] paid set to the payment status of the contract
2856 : * @param[out] wired set to the wire transfer status of the exchange payment
2857 : * @return transaction status
2858 : */
2859 : static enum GNUNET_DB_QueryStatus
2860 0 : postgres_lookup_payment_status (void *cls,
2861 : uint64_t order_serial,
2862 : const char *session_id,
2863 : bool *paid,
2864 : bool *wired)
2865 : {
2866 0 : struct PostgresClosure *pg = cls;
2867 : uint8_t paid8;
2868 : uint8_t wired8;
2869 : enum GNUNET_DB_QueryStatus qs;
2870 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2871 0 : GNUNET_PQ_result_spec_auto_from_type ("paid",
2872 : &paid8),
2873 0 : GNUNET_PQ_result_spec_auto_from_type ("wired",
2874 : &wired8),
2875 : GNUNET_PQ_result_spec_end
2876 : };
2877 0 : check_connection (pg);
2878 0 : if (NULL == session_id)
2879 : {
2880 0 : struct GNUNET_PQ_QueryParam params[] = {
2881 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
2882 : GNUNET_PQ_query_param_end
2883 : };
2884 :
2885 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2886 : "lookup_payment_status",
2887 : params,
2888 : rs);
2889 : }
2890 : else
2891 : {
2892 0 : struct GNUNET_PQ_QueryParam params[] = {
2893 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
2894 0 : GNUNET_PQ_query_param_string (session_id),
2895 : GNUNET_PQ_query_param_end
2896 : };
2897 :
2898 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2899 : "lookup_payment_status_session_id",
2900 : params,
2901 : rs);
2902 : }
2903 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
2904 : {
2905 0 : *paid = (0 != paid8);
2906 0 : *wired = (0 != wired8);
2907 : }
2908 : else
2909 : {
2910 0 : *paid = false; /* just to be safe(r) */
2911 0 : *wired = false; /* just to be safe(r) */
2912 : }
2913 0 : return qs;
2914 : }
2915 :
2916 :
2917 : /**
2918 : * Closure for lookup_deposits_by_order_cb().
2919 : */
2920 : struct LookupDepositsByOrderContext
2921 : {
2922 :
2923 : /**
2924 : * Plugin context.
2925 : */
2926 : struct PostgresClosure *pg;
2927 :
2928 : /**
2929 : * Function to call with all results.
2930 : */
2931 : TALER_MERCHANTDB_DepositedCoinsCallback cb;
2932 :
2933 : /**
2934 : * Closure for @e cb.
2935 : */
2936 : void *cb_cls;
2937 :
2938 : /**
2939 : * Set to the query result.
2940 : */
2941 : enum GNUNET_DB_QueryStatus qs;
2942 : };
2943 :
2944 :
2945 : /**
2946 : * Function to be called with the results of a SELECT statement
2947 : * that has returned @a num_results results.
2948 : *
2949 : * @param cls of type `struct LookupDepositsByOrderContext *`
2950 : * @param result the postgres result
2951 : * @param num_results the number of results in @a result
2952 : */
2953 : static void
2954 0 : lookup_deposits_by_order_cb (void *cls,
2955 : PGresult *result,
2956 : unsigned int num_results)
2957 : {
2958 0 : struct LookupDepositsByOrderContext *ldoc = cls;
2959 0 : struct PostgresClosure *pg = ldoc->pg;
2960 :
2961 0 : for (unsigned int i = 0; i<num_results; i++)
2962 : {
2963 : uint64_t deposit_serial;
2964 : char *exchange_url;
2965 : struct TALER_MerchantWireHashP h_wire;
2966 : struct TALER_CoinSpendPublicKeyP coin_pub;
2967 : struct TALER_Amount amount_with_fee;
2968 : struct TALER_Amount deposit_fee;
2969 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2970 0 : GNUNET_PQ_result_spec_uint64 ("deposit_serial",
2971 : &deposit_serial),
2972 0 : GNUNET_PQ_result_spec_string ("exchange_url",
2973 : &exchange_url),
2974 0 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
2975 : &h_wire),
2976 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
2977 : &amount_with_fee),
2978 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
2979 : &deposit_fee),
2980 0 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
2981 : &coin_pub),
2982 : GNUNET_PQ_result_spec_end
2983 : };
2984 :
2985 0 : if (GNUNET_OK !=
2986 0 : GNUNET_PQ_extract_result (result,
2987 : rs,
2988 : i))
2989 : {
2990 0 : GNUNET_break (0);
2991 0 : ldoc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2992 0 : return;
2993 : }
2994 0 : ldoc->cb (ldoc->cb_cls,
2995 : deposit_serial,
2996 : exchange_url,
2997 : &h_wire,
2998 : &amount_with_fee,
2999 : &deposit_fee,
3000 : &coin_pub);
3001 0 : GNUNET_PQ_cleanup_result (rs); /* technically useless here */
3002 : }
3003 0 : ldoc->qs = num_results;
3004 : }
3005 :
3006 :
3007 : /**
3008 : * Retrieve details about coins that were deposited for an order.
3009 : *
3010 : * @param cls closure
3011 : * @param order_serial identifies the order
3012 : * @param cb function to call for each deposited coin
3013 : * @param cb_cls closure for @a cb
3014 : * @return transaction status
3015 : */
3016 : static enum GNUNET_DB_QueryStatus
3017 0 : postgres_lookup_deposits_by_order (void *cls,
3018 : uint64_t order_serial,
3019 : TALER_MERCHANTDB_DepositedCoinsCallback cb,
3020 : void *cb_cls)
3021 : {
3022 0 : struct PostgresClosure *pg = cls;
3023 0 : struct LookupDepositsByOrderContext ldoc = {
3024 : .pg = pg,
3025 : .cb = cb,
3026 : .cb_cls = cb_cls
3027 : };
3028 0 : struct GNUNET_PQ_QueryParam params[] = {
3029 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
3030 : GNUNET_PQ_query_param_end
3031 : };
3032 : enum GNUNET_DB_QueryStatus qs;
3033 :
3034 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
3035 : "lookup_deposits_by_order",
3036 : params,
3037 : &lookup_deposits_by_order_cb,
3038 : &ldoc);
3039 0 : if (qs < 0)
3040 0 : return qs;
3041 0 : return ldoc.qs;
3042 : }
3043 :
3044 :
3045 : /**
3046 : * Closure for lookup_deposits_by_order_cb().
3047 : */
3048 : struct LookupTransferDetailsByOrderContext
3049 : {
3050 :
3051 : /**
3052 : * Plugin context.
3053 : */
3054 : struct PostgresClosure *pg;
3055 :
3056 : /**
3057 : * Function to call with all results.
3058 : */
3059 : TALER_MERCHANTDB_OrderTransferDetailsCallback cb;
3060 :
3061 : /**
3062 : * Closure for @e cb.
3063 : */
3064 : void *cb_cls;
3065 :
3066 : /**
3067 : * Set to the query result.
3068 : */
3069 : enum GNUNET_DB_QueryStatus qs;
3070 : };
3071 :
3072 :
3073 : /**
3074 : * Function to be called with the results of a SELECT statement
3075 : * that has returned @a num_results results.
3076 : *
3077 : * @param cls of type `struct LookupTransferDetailsByOrderContext *`
3078 : * @param result the postgres result
3079 : * @param num_results the number of results in @a result
3080 : */
3081 : static void
3082 0 : lookup_transfer_details_by_order_cb (void *cls,
3083 : PGresult *result,
3084 : unsigned int num_results)
3085 : {
3086 0 : struct LookupTransferDetailsByOrderContext *ltdo = cls;
3087 0 : struct PostgresClosure *pg = ltdo->pg;
3088 :
3089 0 : for (unsigned int i = 0; i<num_results; i++)
3090 : {
3091 : struct TALER_WireTransferIdentifierRawP wtid;
3092 : char *exchange_url;
3093 : uint64_t deposit_serial;
3094 : struct GNUNET_TIME_Timestamp execution_time;
3095 : struct TALER_Amount deposit_value;
3096 : struct TALER_Amount deposit_fee;
3097 : uint8_t transfer_confirmed;
3098 0 : struct GNUNET_PQ_ResultSpec rs[] = {
3099 0 : GNUNET_PQ_result_spec_uint64 ("deposit_serial",
3100 : &deposit_serial),
3101 0 : GNUNET_PQ_result_spec_timestamp ("deposit_timestamp",
3102 : &execution_time),
3103 0 : GNUNET_PQ_result_spec_string ("exchange_url",
3104 : &exchange_url),
3105 0 : GNUNET_PQ_result_spec_auto_from_type ("wtid",
3106 : &wtid),
3107 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
3108 : &deposit_value),
3109 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
3110 : &deposit_fee),
3111 0 : GNUNET_PQ_result_spec_auto_from_type ("transfer_confirmed",
3112 : &transfer_confirmed),
3113 : GNUNET_PQ_result_spec_end
3114 : };
3115 :
3116 0 : if (GNUNET_OK !=
3117 0 : GNUNET_PQ_extract_result (result,
3118 : rs,
3119 : i))
3120 : {
3121 0 : GNUNET_break (0);
3122 0 : ltdo->qs = GNUNET_DB_STATUS_HARD_ERROR;
3123 0 : return;
3124 : }
3125 0 : ltdo->cb (ltdo->cb_cls,
3126 : &wtid,
3127 : exchange_url,
3128 : execution_time,
3129 : &deposit_value,
3130 : &deposit_fee,
3131 : (0 != transfer_confirmed));
3132 0 : GNUNET_PQ_cleanup_result (rs); /* technically useless here */
3133 : }
3134 0 : ltdo->qs = num_results;
3135 : }
3136 :
3137 :
3138 : /**
3139 : * Retrieve wire transfer details for all deposits associated with
3140 : * a given @a order_serial.
3141 : *
3142 : * @param cls closure
3143 : * @param order_serial identifies the order
3144 : * @param cb function called with the wire transfer details
3145 : * @param cb_cls closure for @a cb
3146 : * @return transaction status
3147 : */
3148 : static enum GNUNET_DB_QueryStatus
3149 0 : postgres_lookup_transfer_details_by_order (
3150 : void *cls,
3151 : uint64_t order_serial,
3152 : TALER_MERCHANTDB_OrderTransferDetailsCallback cb,
3153 : void *cb_cls)
3154 : {
3155 0 : struct PostgresClosure *pg = cls;
3156 0 : struct LookupTransferDetailsByOrderContext ltdo = {
3157 : .pg = pg,
3158 : .cb = cb,
3159 : .cb_cls = cb_cls
3160 : };
3161 0 : struct GNUNET_PQ_QueryParam params[] = {
3162 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
3163 : GNUNET_PQ_query_param_end
3164 : };
3165 : enum GNUNET_DB_QueryStatus qs;
3166 :
3167 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
3168 : pg->conn,
3169 : "lookup_transfer_details_by_order",
3170 : params,
3171 : &lookup_transfer_details_by_order_cb,
3172 : <do);
3173 0 : if (qs < 0)
3174 0 : return qs;
3175 0 : return ltdo.qs;
3176 : }
3177 :
3178 :
3179 : /**
3180 : * Insert wire transfer details for a deposit.
3181 : *
3182 : * @param cls closure
3183 : * @param deposit_serial serial number of the deposit
3184 : * @param dd deposit transfer data from the exchange to store
3185 : * @return transaction status
3186 : */
3187 : static enum GNUNET_DB_QueryStatus
3188 0 : postgres_insert_deposit_to_transfer (
3189 : void *cls,
3190 : uint64_t deposit_serial,
3191 : const struct TALER_EXCHANGE_DepositData *dd)
3192 : {
3193 0 : struct PostgresClosure *pg = cls;
3194 0 : struct GNUNET_PQ_QueryParam params[] = {
3195 0 : GNUNET_PQ_query_param_uint64 (&deposit_serial),
3196 0 : TALER_PQ_query_param_amount (&dd->coin_contribution),
3197 0 : GNUNET_PQ_query_param_timestamp (&dd->execution_time),
3198 0 : GNUNET_PQ_query_param_auto_from_type (&dd->exchange_sig),
3199 0 : GNUNET_PQ_query_param_auto_from_type (&dd->exchange_pub),
3200 0 : GNUNET_PQ_query_param_auto_from_type (&dd->wtid),
3201 : GNUNET_PQ_query_param_end
3202 : };
3203 :
3204 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3205 : "insert_deposit_to_transfer",
3206 : params);
3207 : }
3208 :
3209 :
3210 : /**
3211 : * Set 'wired' status for an order to 'true'.
3212 : *
3213 : * @param cls closure
3214 : * @param order_serial serial number of the order
3215 : * @return transaction status
3216 : */
3217 : static enum GNUNET_DB_QueryStatus
3218 0 : postgres_mark_order_wired (void *cls,
3219 : uint64_t order_serial)
3220 : {
3221 0 : struct PostgresClosure *pg = cls;
3222 0 : struct GNUNET_PQ_QueryParam params[] = {
3223 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
3224 : GNUNET_PQ_query_param_end
3225 : };
3226 :
3227 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3228 : "mark_order_wired",
3229 : params);
3230 : }
3231 :
3232 :
3233 : /**
3234 : * Closure for #process_refund_cb().
3235 : */
3236 : struct FindRefundContext
3237 : {
3238 :
3239 : /**
3240 : * Plugin context.
3241 : */
3242 : struct PostgresClosure *pg;
3243 :
3244 : /**
3245 : * Updated to reflect total amount refunded so far.
3246 : */
3247 : struct TALER_Amount refunded_amount;
3248 :
3249 : /**
3250 : * Set to the largest refund transaction ID encountered.
3251 : */
3252 : uint64_t max_rtransaction_id;
3253 :
3254 : /**
3255 : * Set to true on hard errors.
3256 : */
3257 : bool err;
3258 : };
3259 :
3260 :
3261 : /**
3262 : * Function to be called with the results of a SELECT statement
3263 : * that has returned @a num_results results.
3264 : *
3265 : * @param cls closure, our `struct FindRefundContext`
3266 : * @param result the postgres result
3267 : * @param num_results the number of results in @a result
3268 : */
3269 : static void
3270 0 : process_refund_cb (void *cls,
3271 : PGresult *result,
3272 : unsigned int num_results)
3273 : {
3274 0 : struct FindRefundContext *ictx = cls;
3275 0 : struct PostgresClosure *pg = ictx->pg;
3276 :
3277 0 : for (unsigned int i = 0; i<num_results; i++)
3278 : {
3279 : /* Sum up existing refunds */
3280 : struct TALER_Amount acc;
3281 : uint64_t rtransaction_id;
3282 0 : struct GNUNET_PQ_ResultSpec rs[] = {
3283 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
3284 : &acc),
3285 0 : GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
3286 : &rtransaction_id),
3287 : GNUNET_PQ_result_spec_end
3288 : };
3289 :
3290 0 : if (GNUNET_OK !=
3291 0 : GNUNET_PQ_extract_result (result,
3292 : rs,
3293 : i))
3294 : {
3295 0 : GNUNET_break (0);
3296 0 : ictx->err = true;
3297 0 : return;
3298 : }
3299 0 : if (0 >
3300 0 : TALER_amount_add (&ictx->refunded_amount,
3301 0 : &ictx->refunded_amount,
3302 : &acc))
3303 : {
3304 0 : GNUNET_break (0);
3305 0 : ictx->err = true;
3306 0 : return;
3307 : }
3308 0 : ictx->max_rtransaction_id = GNUNET_MAX (ictx->max_rtransaction_id,
3309 : rtransaction_id);
3310 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3311 : "Found refund of %s\n",
3312 : TALER_amount2s (&acc));
3313 : }
3314 : }
3315 :
3316 :
3317 : /**
3318 : * Closure for #process_deposits_for_refund_cb().
3319 : */
3320 : struct InsertRefundContext
3321 : {
3322 : /**
3323 : * Used to provide a connection to the db
3324 : */
3325 : struct PostgresClosure *pg;
3326 :
3327 : /**
3328 : * Amount to which increase the refund for this contract
3329 : */
3330 : const struct TALER_Amount *refund;
3331 :
3332 : /**
3333 : * Human-readable reason behind this refund
3334 : */
3335 : const char *reason;
3336 :
3337 : /**
3338 : * Transaction status code.
3339 : */
3340 : enum TALER_MERCHANTDB_RefundStatus rs;
3341 : };
3342 :
3343 :
3344 : /**
3345 : * Data extracted per coin.
3346 : */
3347 : struct RefundCoinData
3348 : {
3349 :
3350 : /**
3351 : * Public key of a coin.
3352 : */
3353 : struct TALER_CoinSpendPublicKeyP coin_pub;
3354 :
3355 : /**
3356 : * Amount deposited for this coin.
3357 : */
3358 : struct TALER_Amount deposited_with_fee;
3359 :
3360 : /**
3361 : * Amount refunded already for this coin.
3362 : */
3363 : struct TALER_Amount refund_amount;
3364 :
3365 : /**
3366 : * Order serial (actually not really per-coin).
3367 : */
3368 : uint64_t order_serial;
3369 :
3370 : /**
3371 : * Maximum rtransaction_id for this coin so far.
3372 : */
3373 : uint64_t max_rtransaction_id;
3374 :
3375 : };
3376 :
3377 :
3378 : /**
3379 : * Function to be called with the results of a SELECT statement
3380 : * that has returned @a num_results results.
3381 : *
3382 : * @param cls closure, our `struct InsertRefundContext`
3383 : * @param result the postgres result
3384 : * @param num_results the number of results in @a result
3385 : */
3386 : static void
3387 0 : process_deposits_for_refund_cb (void *cls,
3388 : PGresult *result,
3389 : unsigned int num_results)
3390 0 : {
3391 0 : struct InsertRefundContext *ctx = cls;
3392 0 : struct PostgresClosure *pg = ctx->pg;
3393 : struct TALER_Amount current_refund;
3394 0 : struct RefundCoinData rcd[GNUNET_NZL (num_results)];
3395 : struct GNUNET_TIME_Timestamp now;
3396 :
3397 0 : now = GNUNET_TIME_timestamp_get ();
3398 0 : GNUNET_assert (GNUNET_OK ==
3399 : TALER_amount_set_zero (ctx->refund->currency,
3400 : ¤t_refund));
3401 0 : memset (rcd, 0, sizeof (rcd));
3402 : /* Pass 1: Collect amount of existing refunds into current_refund.
3403 : * Also store existing refunded amount for each deposit in deposit_refund. */
3404 0 : for (unsigned int i = 0; i<num_results; i++)
3405 : {
3406 0 : struct GNUNET_PQ_ResultSpec rs[] = {
3407 0 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
3408 : &rcd[i].coin_pub),
3409 0 : GNUNET_PQ_result_spec_uint64 ("order_serial",
3410 : &rcd[i].order_serial),
3411 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
3412 : &rcd[i].deposited_with_fee),
3413 : GNUNET_PQ_result_spec_end
3414 : };
3415 0 : struct FindRefundContext ictx = {
3416 : .pg = pg
3417 : };
3418 :
3419 0 : if (GNUNET_OK !=
3420 0 : GNUNET_PQ_extract_result (result,
3421 : rs,
3422 : i))
3423 : {
3424 0 : GNUNET_break (0);
3425 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3426 0 : return;
3427 : }
3428 :
3429 : {
3430 : enum GNUNET_DB_QueryStatus ires;
3431 0 : struct GNUNET_PQ_QueryParam params[] = {
3432 0 : GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
3433 0 : GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
3434 : GNUNET_PQ_query_param_end
3435 : };
3436 :
3437 0 : GNUNET_assert (GNUNET_OK ==
3438 : TALER_amount_set_zero (ctx->refund->currency,
3439 : &ictx.refunded_amount));
3440 0 : ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
3441 : "find_refunds_by_coin",
3442 : params,
3443 : &process_refund_cb,
3444 : &ictx);
3445 0 : if ( (ictx.err) ||
3446 : (GNUNET_DB_STATUS_HARD_ERROR == ires) )
3447 : {
3448 0 : GNUNET_break (0);
3449 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3450 0 : return;
3451 : }
3452 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
3453 : {
3454 0 : ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
3455 0 : return;
3456 : }
3457 : }
3458 0 : if (0 >
3459 0 : TALER_amount_add (¤t_refund,
3460 : ¤t_refund,
3461 : &ictx.refunded_amount))
3462 : {
3463 0 : GNUNET_break (0);
3464 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3465 0 : return;
3466 : }
3467 0 : rcd[i].refund_amount = ictx.refunded_amount;
3468 0 : rcd[i].max_rtransaction_id = ictx.max_rtransaction_id;
3469 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3470 : "Existing refund for coin %s is %s\n",
3471 : TALER_B2S (&rcd[i].coin_pub),
3472 : TALER_amount2s (&ictx.refunded_amount));
3473 : }
3474 :
3475 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3476 : "Total existing refund is %s\n",
3477 : TALER_amount2s (¤t_refund));
3478 :
3479 : /* stop immediately if we are 'done' === amount already
3480 : * refunded. */
3481 0 : if (0 >= TALER_amount_cmp (ctx->refund,
3482 : ¤t_refund))
3483 : {
3484 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3485 : "Existing refund of %s at or above requested refund. Finished early.\n",
3486 : TALER_amount2s (¤t_refund));
3487 0 : ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
3488 0 : return;
3489 : }
3490 :
3491 : /* Phase 2: Try to increase current refund until it matches desired refund */
3492 0 : for (unsigned int i = 0; i<num_results; i++)
3493 : {
3494 : const struct TALER_Amount *increment;
3495 : struct TALER_Amount left;
3496 : struct TALER_Amount remaining_refund;
3497 :
3498 : /* How much of the coin is left after the existing refunds? */
3499 0 : if (0 >
3500 0 : TALER_amount_subtract (&left,
3501 0 : &rcd[i].deposited_with_fee,
3502 0 : &rcd[i].refund_amount))
3503 : {
3504 0 : GNUNET_break (0);
3505 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3506 0 : return;
3507 : }
3508 :
3509 0 : if ( (0 == left.value) &&
3510 0 : (0 == left.fraction) )
3511 : {
3512 : /* coin was fully refunded, move to next coin */
3513 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3514 : "Coin %s fully refunded, moving to next coin\n",
3515 : TALER_B2S (&rcd[i].coin_pub));
3516 0 : continue;
3517 : }
3518 :
3519 0 : rcd[i].max_rtransaction_id++;
3520 : /* How much of the refund is still to be paid back? */
3521 0 : if (0 >
3522 0 : TALER_amount_subtract (&remaining_refund,
3523 : ctx->refund,
3524 : ¤t_refund))
3525 : {
3526 0 : GNUNET_break (0);
3527 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3528 0 : return;
3529 : }
3530 :
3531 : /* By how much will we increase the refund for this coin? */
3532 0 : if (0 >= TALER_amount_cmp (&remaining_refund,
3533 : &left))
3534 : {
3535 : /* remaining_refund <= left */
3536 0 : increment = &remaining_refund;
3537 : }
3538 : else
3539 : {
3540 0 : increment = &left;
3541 : }
3542 :
3543 0 : if (0 >
3544 0 : TALER_amount_add (¤t_refund,
3545 : ¤t_refund,
3546 : increment))
3547 : {
3548 0 : GNUNET_break (0);
3549 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3550 0 : return;
3551 : }
3552 :
3553 : /* actually run the refund */
3554 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3555 : "Coin %s deposit amount is %s\n",
3556 : TALER_B2S (&rcd[i].coin_pub),
3557 : TALER_amount2s (&rcd[i].deposited_with_fee));
3558 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3559 : "Coin %s refund will be incremented by %s\n",
3560 : TALER_B2S (&rcd[i].coin_pub),
3561 : TALER_amount2s (increment));
3562 : {
3563 : enum GNUNET_DB_QueryStatus qs;
3564 0 : struct GNUNET_PQ_QueryParam params[] = {
3565 0 : GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
3566 0 : GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already inc'ed */
3567 0 : GNUNET_PQ_query_param_timestamp (&now),
3568 0 : GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
3569 0 : GNUNET_PQ_query_param_string (ctx->reason),
3570 0 : TALER_PQ_query_param_amount (increment),
3571 : GNUNET_PQ_query_param_end
3572 : };
3573 :
3574 0 : check_connection (pg);
3575 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
3576 : "insert_refund",
3577 : params);
3578 0 : switch (qs)
3579 : {
3580 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3581 0 : GNUNET_break (0);
3582 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3583 0 : return;
3584 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
3585 0 : ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
3586 0 : return;
3587 0 : default:
3588 0 : ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs;
3589 0 : break;
3590 : }
3591 : }
3592 :
3593 : /* stop immediately if we are done */
3594 0 : if (0 == TALER_amount_cmp (ctx->refund,
3595 : ¤t_refund))
3596 : {
3597 0 : ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
3598 0 : return;
3599 : }
3600 : }
3601 :
3602 : /**
3603 : * We end up here if not all of the refund has been covered.
3604 : * Although this should be checked as the business should never
3605 : * issue a refund bigger than the contract's actual price, we cannot
3606 : * rely upon the frontend being correct.
3607 : *///
3608 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3609 : "The refund of %s is bigger than the order's value\n",
3610 : TALER_amount2s (ctx->refund));
3611 0 : ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH;
3612 : }
3613 :
3614 :
3615 : /**
3616 : * Function called when some backoffice staff decides to award or
3617 : * increase the refund on an existing contract. This function
3618 : * MUST be called from within a transaction scope setup by the
3619 : * caller as it executes multiple SQL statements.
3620 : *
3621 : * @param cls closure
3622 : * @param instance_id instance identifier
3623 : * @param order_id the order to increase the refund for
3624 : * @param refund maximum refund to return to the customer for this contract
3625 : * @param reason 0-terminated UTF-8 string giving the reason why the customer
3626 : * got a refund (free form, business-specific)
3627 : * @return transaction status
3628 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we
3629 : * were originally paid and thus the transaction failed;
3630 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
3631 : * regardless of whether it actually increased the refund beyond
3632 : * what was already refunded (idempotency!)
3633 : */
3634 : static enum TALER_MERCHANTDB_RefundStatus
3635 0 : postgres_increase_refund (void *cls,
3636 : const char *instance_id,
3637 : const char *order_id,
3638 : const struct TALER_Amount *refund,
3639 : const char *reason)
3640 : {
3641 0 : struct PostgresClosure *pg = cls;
3642 : enum GNUNET_DB_QueryStatus qs;
3643 0 : struct GNUNET_PQ_QueryParam params[] = {
3644 0 : GNUNET_PQ_query_param_string (instance_id),
3645 0 : GNUNET_PQ_query_param_string (order_id),
3646 : GNUNET_PQ_query_param_end
3647 : };
3648 0 : struct InsertRefundContext ctx = {
3649 : .pg = pg,
3650 : .refund = refund,
3651 : .reason = reason
3652 : };
3653 :
3654 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3655 : "Asked to refund %s on order %s\n",
3656 : TALER_amount2s (refund),
3657 : order_id);
3658 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
3659 : "find_deposits_for_refund",
3660 : params,
3661 : &process_deposits_for_refund_cb,
3662 : &ctx);
3663 0 : switch (qs)
3664 : {
3665 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3666 : /* never paid, means we clearly cannot refund anything */
3667 0 : return TALER_MERCHANTDB_RS_NO_SUCH_ORDER;
3668 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
3669 0 : return TALER_MERCHANTDB_RS_SOFT_ERROR;
3670 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3671 0 : return TALER_MERCHANTDB_RS_HARD_ERROR;
3672 0 : default:
3673 : /* Got one or more deposits */
3674 0 : return ctx.rs;
3675 : }
3676 : }
3677 :
3678 :
3679 : /**
3680 : * Closure for #lookup_refunds_detailed_cb().
3681 : */
3682 : struct LookupRefundsDetailedContext
3683 : {
3684 : /**
3685 : * Function to call for each refund.
3686 : */
3687 : TALER_MERCHANTDB_RefundDetailCallback rc;
3688 :
3689 : /**
3690 : * Closure for @e rc.
3691 : */
3692 : void *rc_cls;
3693 :
3694 : /**
3695 : * Plugin context.
3696 : */
3697 : struct PostgresClosure *pg;
3698 :
3699 : /**
3700 : * Transaction result.
3701 : */
3702 : enum GNUNET_DB_QueryStatus qs;
3703 : };
3704 :
3705 :
3706 : /**
3707 : * Function to be called with the results of a SELECT statement
3708 : * that has returned @a num_results results.
3709 : *
3710 : * @param cls of type `struct GetRefundsContext *`
3711 : * @param result the postgres result
3712 : * @param num_results the number of results in @a result
3713 : */
3714 : static void
3715 0 : lookup_refunds_detailed_cb (void *cls,
3716 : PGresult *result,
3717 : unsigned int num_results)
3718 : {
3719 0 : struct LookupRefundsDetailedContext *lrdc = cls;
3720 0 : struct PostgresClosure *pg = lrdc->pg;
3721 :
3722 0 : for (unsigned int i = 0; i<num_results; i++)
3723 : {
3724 : uint64_t refund_serial;
3725 : struct GNUNET_TIME_Timestamp timestamp;
3726 : struct TALER_CoinSpendPublicKeyP coin_pub;
3727 : uint64_t rtransaction_id;
3728 : struct TALER_Amount refund_amount;
3729 : char *reason;
3730 : char *exchange_url;
3731 : uint8_t pending8;
3732 0 : struct GNUNET_PQ_ResultSpec rs[] = {
3733 0 : GNUNET_PQ_result_spec_uint64 ("refund_serial",
3734 : &refund_serial),
3735 0 : GNUNET_PQ_result_spec_timestamp ("refund_timestamp",
3736 : ×tamp),
3737 0 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
3738 : &coin_pub),
3739 0 : GNUNET_PQ_result_spec_string ("exchange_url",
3740 : &exchange_url),
3741 0 : GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
3742 : &rtransaction_id),
3743 0 : GNUNET_PQ_result_spec_string ("reason",
3744 : &reason),
3745 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
3746 : &refund_amount),
3747 0 : GNUNET_PQ_result_spec_auto_from_type ("pending",
3748 : &pending8),
3749 : GNUNET_PQ_result_spec_end
3750 : };
3751 :
3752 0 : if (GNUNET_OK !=
3753 0 : GNUNET_PQ_extract_result (result,
3754 : rs,
3755 : i))
3756 : {
3757 0 : GNUNET_break (0);
3758 0 : lrdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
3759 0 : return;
3760 : }
3761 0 : lrdc->rc (lrdc->rc_cls,
3762 : refund_serial,
3763 : timestamp,
3764 : &coin_pub,
3765 : exchange_url,
3766 : rtransaction_id,
3767 : reason,
3768 : &refund_amount,
3769 : 0 != pending8);
3770 0 : GNUNET_PQ_cleanup_result (rs);
3771 : }
3772 0 : lrdc->qs = num_results;
3773 : }
3774 :
3775 :
3776 : /**
3777 : * Obtain detailed refund data associated with a contract.
3778 : *
3779 : * @param cls closure, typically a connection to the db
3780 : * @param instance_id instance to lookup refunds for
3781 : * @param h_contract_terms hash code of the contract
3782 : * @param rc function to call for each coin on which there is a refund
3783 : * @param rc_cls closure for @a rc
3784 : * @return transaction status
3785 : */
3786 : static enum GNUNET_DB_QueryStatus
3787 0 : postgres_lookup_refunds_detailed (
3788 : void *cls,
3789 : const char *instance_id,
3790 : const struct TALER_PrivateContractHashP *h_contract_terms,
3791 : TALER_MERCHANTDB_RefundDetailCallback rc,
3792 : void *rc_cls)
3793 : {
3794 0 : struct PostgresClosure *pg = cls;
3795 0 : struct GNUNET_PQ_QueryParam params[] = {
3796 0 : GNUNET_PQ_query_param_string (instance_id),
3797 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
3798 : GNUNET_PQ_query_param_end
3799 : };
3800 0 : struct LookupRefundsDetailedContext lrdc = {
3801 : .rc = rc,
3802 : .rc_cls = rc_cls,
3803 : .pg = pg
3804 : };
3805 : enum GNUNET_DB_QueryStatus qs;
3806 :
3807 : /* no preflight check here, run in transaction by caller! */
3808 0 : TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
3809 : GNUNET_h2s (&h_contract_terms->hash),
3810 : instance_id);
3811 0 : check_connection (pg);
3812 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
3813 : "lookup_refunds_detailed",
3814 : params,
3815 : &lookup_refunds_detailed_cb,
3816 : &lrdc);
3817 0 : if (0 >= qs)
3818 0 : return qs;
3819 0 : return lrdc.qs;
3820 : }
3821 :
3822 :
3823 : /**
3824 : * Insert refund proof data from the exchange into the database.
3825 : *
3826 : * @param cls closure
3827 : * @param refund_serial serial number of the refund
3828 : * @param exchange_sig signature from exchange that coin was refunded
3829 : * @param exchange_pub signing key that was used for @a exchange_sig
3830 : * @return transaction status
3831 : */
3832 : static enum GNUNET_DB_QueryStatus
3833 0 : postgres_insert_refund_proof (
3834 : void *cls,
3835 : uint64_t refund_serial,
3836 : const struct TALER_ExchangeSignatureP *exchange_sig,
3837 : const struct TALER_ExchangePublicKeyP *exchange_pub)
3838 : {
3839 0 : struct PostgresClosure *pg = cls;
3840 0 : struct GNUNET_PQ_QueryParam params[] = {
3841 0 : GNUNET_PQ_query_param_uint64 (&refund_serial),
3842 0 : GNUNET_PQ_query_param_auto_from_type (exchange_sig),
3843 0 : GNUNET_PQ_query_param_auto_from_type (exchange_pub),
3844 : GNUNET_PQ_query_param_end
3845 : };
3846 :
3847 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3848 : "insert_refund_proof",
3849 : params);
3850 : }
3851 :
3852 :
3853 : /**
3854 : * Lookup refund proof data.
3855 : *
3856 : * @param cls closure
3857 : * @param refund_serial serial number of the refund
3858 : * @param[out] exchange_sig set to signature from exchange
3859 : * @param[out] exchange_pub signing key that was used for @a exchange_sig
3860 : * @return transaction status
3861 : */
3862 : static enum GNUNET_DB_QueryStatus
3863 0 : postgres_lookup_refund_proof (void *cls,
3864 : uint64_t refund_serial,
3865 : struct TALER_ExchangeSignatureP *exchange_sig,
3866 : struct TALER_ExchangePublicKeyP *exchange_pub)
3867 : {
3868 0 : struct PostgresClosure *pg = cls;
3869 0 : struct GNUNET_PQ_QueryParam params[] = {
3870 0 : GNUNET_PQ_query_param_uint64 (&refund_serial),
3871 : GNUNET_PQ_query_param_end
3872 : };
3873 0 : struct GNUNET_PQ_ResultSpec rs[] = {
3874 0 : GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
3875 : exchange_sig),
3876 0 : GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
3877 : exchange_pub),
3878 : GNUNET_PQ_result_spec_end
3879 : };
3880 :
3881 0 : check_connection (pg);
3882 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
3883 : "lookup_refund_proof",
3884 : params,
3885 : rs);
3886 : }
3887 :
3888 :
3889 : /**
3890 : * Retrieve the order ID that was used to pay for a resource within a session.
3891 : *
3892 : * @param cls closure
3893 : * @param instance_id identifying the instance
3894 : * @param fulfillment_url URL that canonically identifies the resource
3895 : * being paid for
3896 : * @param session_id session id
3897 : * @param[out] order_id where to store the order ID that was used when
3898 : * paying for the resource URL
3899 : * @return transaction status
3900 : */
3901 : enum GNUNET_DB_QueryStatus
3902 0 : postgres_lookup_order_by_fulfillment (void *cls,
3903 : const char *instance_id,
3904 : const char *fulfillment_url,
3905 : const char *session_id,
3906 : char **order_id)
3907 : {
3908 0 : struct PostgresClosure *pg = cls;
3909 0 : struct GNUNET_PQ_QueryParam params[] = {
3910 0 : GNUNET_PQ_query_param_string (instance_id),
3911 0 : GNUNET_PQ_query_param_string (fulfillment_url),
3912 0 : GNUNET_PQ_query_param_string (session_id),
3913 : GNUNET_PQ_query_param_end
3914 : };
3915 0 : struct GNUNET_PQ_ResultSpec rs[] = {
3916 0 : GNUNET_PQ_result_spec_string ("order_id",
3917 : order_id),
3918 : GNUNET_PQ_result_spec_end
3919 : };
3920 :
3921 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
3922 : "lookup_order_by_fulfillment",
3923 : params,
3924 : rs);
3925 : }
3926 :
3927 :
3928 : /**
3929 : * Insert information about a wire transfer the merchant has received.
3930 : *
3931 : * @param cls closure
3932 : * @param instance_id the instance that received the transfer
3933 : * @param exchange_url which exchange made the transfer
3934 : * @param wtid identifier of the wire transfer
3935 : * @param credit_amount how much did we receive
3936 : * @param payto_uri what is the merchant's bank account that received the transfer
3937 : * @param confirmed whether the transfer was confirmed by the merchant or
3938 : * was merely claimed by the exchange at this point
3939 : * @return transaction status
3940 : */
3941 : static enum GNUNET_DB_QueryStatus
3942 0 : postgres_insert_transfer (
3943 : void *cls,
3944 : const char *instance_id,
3945 : const char *exchange_url,
3946 : const struct TALER_WireTransferIdentifierRawP *wtid,
3947 : const struct TALER_Amount *credit_amount,
3948 : const char *payto_uri,
3949 : bool confirmed)
3950 : {
3951 0 : struct PostgresClosure *pg = cls;
3952 0 : uint8_t confirmed8 = confirmed;
3953 0 : struct GNUNET_PQ_QueryParam params[] = {
3954 0 : GNUNET_PQ_query_param_string (exchange_url),
3955 0 : GNUNET_PQ_query_param_auto_from_type (wtid),
3956 0 : TALER_PQ_query_param_amount (credit_amount),
3957 0 : GNUNET_PQ_query_param_string (payto_uri),
3958 0 : GNUNET_PQ_query_param_auto_from_type (&confirmed8),
3959 0 : GNUNET_PQ_query_param_string (instance_id),
3960 : GNUNET_PQ_query_param_end
3961 : };
3962 :
3963 0 : check_connection (pg);
3964 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3965 : "insert_transfer",
3966 : params);
3967 : }
3968 :
3969 :
3970 : /**
3971 : * Delete information about a transfer. Note that transfers
3972 : * confirmed by the exchange cannot be deleted anymore.
3973 : *
3974 : * @param cls closure
3975 : * @param instance_id instance to delete transfer of
3976 : * @param transfer_serial_id transfer to delete
3977 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
3978 : * if deletion is prohibited OR transfer is unknown
3979 : */
3980 : static enum GNUNET_DB_QueryStatus
3981 0 : postgres_delete_transfer (void *cls,
3982 : const char *instance_id,
3983 : uint64_t transfer_serial_id)
3984 : {
3985 0 : struct PostgresClosure *pg = cls;
3986 0 : struct GNUNET_PQ_QueryParam params[] = {
3987 0 : GNUNET_PQ_query_param_string (instance_id),
3988 0 : GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
3989 : GNUNET_PQ_query_param_end
3990 : };
3991 :
3992 0 : check_connection (pg);
3993 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3994 : "delete_transfer",
3995 : params);
3996 : }
3997 :
3998 :
3999 : /**
4000 : * Check if information about a transfer exists with the
4001 : * backend. Returns no data, only the query status.
4002 : *
4003 : * @param cls closure
4004 : * @param instance_id instance to delete transfer of
4005 : * @param transfer_serial_id transfer to delete
4006 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
4007 : * if the transfer record exists
4008 : */
4009 : static enum GNUNET_DB_QueryStatus
4010 0 : postgres_check_transfer_exists (void *cls,
4011 : const char *instance_id,
4012 : uint64_t transfer_serial_id)
4013 : {
4014 0 : struct PostgresClosure *pg = cls;
4015 0 : struct GNUNET_PQ_QueryParam params[] = {
4016 0 : GNUNET_PQ_query_param_string (instance_id),
4017 0 : GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
4018 : GNUNET_PQ_query_param_end
4019 : };
4020 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4021 : GNUNET_PQ_result_spec_end
4022 : };
4023 :
4024 0 : check_connection (pg);
4025 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4026 : "check_transfer_exists",
4027 : params,
4028 : rs);
4029 : }
4030 :
4031 :
4032 : /**
4033 : * Lookup account serial by payto URI.
4034 : *
4035 : * @param cls closure
4036 : * @param instance_id instance to lookup the account from
4037 : * @param payto_uri what is the merchant's bank account to lookup
4038 : * @param[out] account_serial serial number of the account
4039 : * @return transaction status
4040 : */
4041 : static enum GNUNET_DB_QueryStatus
4042 0 : postgres_lookup_account (void *cls,
4043 : const char *instance_id,
4044 : const char *payto_uri,
4045 : uint64_t *account_serial)
4046 : {
4047 0 : struct PostgresClosure *pg = cls;
4048 0 : struct GNUNET_PQ_QueryParam params[] = {
4049 0 : GNUNET_PQ_query_param_string (instance_id),
4050 0 : GNUNET_PQ_query_param_string (payto_uri),
4051 : GNUNET_PQ_query_param_end
4052 : };
4053 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4054 0 : GNUNET_PQ_result_spec_uint64 ("account_serial",
4055 : account_serial),
4056 : GNUNET_PQ_result_spec_end
4057 : };
4058 :
4059 0 : check_connection (pg);
4060 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4061 : "lookup_account",
4062 : params,
4063 : rs);
4064 : }
4065 :
4066 :
4067 : /**
4068 : * Insert information about a wire transfer the merchant has received.
4069 : *
4070 : * @param cls closure
4071 : * @param instance_id instance to provide transfer details for
4072 : * @param exchange_url which exchange made the transfer
4073 : * @param payto_uri what is the merchant's bank account that received the transfer
4074 : * @param wtid identifier of the wire transfer
4075 : * @param td transfer details to store
4076 : * @return transaction status,
4077 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
4078 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
4079 : */
4080 : static enum GNUNET_DB_QueryStatus
4081 0 : postgres_insert_transfer_details (
4082 : void *cls,
4083 : const char *instance_id,
4084 : const char *exchange_url,
4085 : const char *payto_uri,
4086 : const struct TALER_WireTransferIdentifierRawP *wtid,
4087 : const struct TALER_EXCHANGE_TransferData *td)
4088 : {
4089 0 : struct PostgresClosure *pg = cls;
4090 : enum GNUNET_DB_QueryStatus qs;
4091 : uint64_t credit_serial;
4092 : unsigned int retries;
4093 :
4094 0 : retries = 0;
4095 0 : check_connection (pg);
4096 0 : RETRY:
4097 0 : if (MAX_RETRIES < ++retries)
4098 0 : return GNUNET_DB_STATUS_SOFT_ERROR;
4099 0 : if (GNUNET_OK !=
4100 0 : postgres_start_read_committed (pg,
4101 : "insert transfer details"))
4102 : {
4103 0 : GNUNET_break (0);
4104 0 : return GNUNET_DB_STATUS_HARD_ERROR;
4105 : }
4106 :
4107 : /* lookup credit serial */
4108 : {
4109 0 : struct GNUNET_PQ_QueryParam params[] = {
4110 0 : GNUNET_PQ_query_param_string (exchange_url),
4111 0 : GNUNET_PQ_query_param_string (payto_uri),
4112 0 : GNUNET_PQ_query_param_string (instance_id),
4113 0 : GNUNET_PQ_query_param_auto_from_type (wtid),
4114 : GNUNET_PQ_query_param_end
4115 : };
4116 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4117 0 : GNUNET_PQ_result_spec_uint64 ("credit_serial",
4118 : &credit_serial),
4119 : GNUNET_PQ_result_spec_end
4120 : };
4121 :
4122 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4123 : "lookup_credit_serial",
4124 : params,
4125 : rs);
4126 0 : if (0 > qs)
4127 : {
4128 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4129 0 : postgres_rollback (pg);
4130 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4131 0 : goto RETRY;
4132 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4133 : "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
4134 : payto_uri,
4135 : TALER_amount2s (&td->total_amount),
4136 : qs);
4137 0 : return qs;
4138 : }
4139 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
4140 : {
4141 0 : postgres_rollback (pg);
4142 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4143 : "'lookup_credit_serial' for account %s failed with transfer unknown\n",
4144 : payto_uri);
4145 0 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
4146 : }
4147 : }
4148 :
4149 : /* update merchant_transfer_signatures table */
4150 : {
4151 0 : struct GNUNET_PQ_QueryParam params[] = {
4152 0 : GNUNET_PQ_query_param_uint64 (&credit_serial),
4153 0 : TALER_PQ_query_param_amount (&td->total_amount),
4154 0 : TALER_PQ_query_param_amount (&td->wire_fee),
4155 0 : GNUNET_PQ_query_param_timestamp (&td->execution_time),
4156 0 : GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
4157 0 : GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
4158 : GNUNET_PQ_query_param_end
4159 : };
4160 :
4161 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
4162 : "insert_transfer_signature",
4163 : params);
4164 0 : if (0 > qs)
4165 : {
4166 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4167 0 : postgres_rollback (pg);
4168 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4169 0 : goto RETRY;
4170 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4171 : "'insert_transfer_signature' failed with status %d\n",
4172 : qs);
4173 0 : return qs;
4174 : }
4175 0 : if (0 == qs)
4176 : {
4177 0 : postgres_rollback (pg);
4178 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4179 : "'insert_transfer_signature' failed with status %d\n",
4180 : qs);
4181 0 : return GNUNET_DB_STATUS_HARD_ERROR;
4182 : }
4183 : }
4184 :
4185 : /* Update transfer-coin association table */
4186 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4187 : "Updating transfer-coin association table\n");
4188 0 : for (unsigned int i = 0; i<td->details_length; i++)
4189 : {
4190 0 : const struct TALER_TrackTransferDetails *d = &td->details[i];
4191 0 : uint64_t i64 = (uint64_t) i;
4192 0 : struct GNUNET_PQ_QueryParam params[] = {
4193 0 : GNUNET_PQ_query_param_uint64 (&credit_serial),
4194 0 : GNUNET_PQ_query_param_uint64 (&i64),
4195 0 : TALER_PQ_query_param_amount (&d->coin_value),
4196 0 : TALER_PQ_query_param_amount (&d->coin_fee), /* deposit fee */
4197 0 : GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
4198 0 : GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms),
4199 0 : GNUNET_PQ_query_param_string (instance_id),
4200 : GNUNET_PQ_query_param_end
4201 : };
4202 :
4203 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
4204 : "insert_transfer_to_coin_mapping",
4205 : params);
4206 0 : if (0 > qs)
4207 : {
4208 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4209 0 : postgres_rollback (pg);
4210 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4211 0 : goto RETRY;
4212 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4213 : "'insert_transfer_to_coin_mapping' failed with status %d\n",
4214 : qs);
4215 0 : return qs;
4216 : }
4217 0 : if (0 == qs)
4218 : {
4219 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4220 : "'insert_transfer_to_coin_mapping' failed at %u: deposit unknown\n",
4221 : i);
4222 : }
4223 : }
4224 : /* Update merchant_contract_terms 'wired' status: for all coins
4225 : that were wired, set the respective order's "wired" status to
4226 : true, *if* all other deposited coins associated with that order
4227 : have also been wired (this time or earlier) */
4228 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4229 : "Updating contract terms 'wired' status\n");
4230 0 : for (unsigned int i = 0; i<td->details_length; i++)
4231 : {
4232 0 : const struct TALER_TrackTransferDetails *d = &td->details[i];
4233 0 : struct GNUNET_PQ_QueryParam params[] = {
4234 0 : GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
4235 : GNUNET_PQ_query_param_end
4236 : };
4237 :
4238 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
4239 : "update_wired_by_coin_pub",
4240 : params);
4241 0 : if (0 > qs)
4242 : {
4243 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4244 0 : postgres_rollback (pg);
4245 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4246 0 : goto RETRY;
4247 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4248 : "'update_wired_by_coin_pub' failed with status %d\n",
4249 : qs);
4250 0 : return qs;
4251 : }
4252 : }
4253 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4254 : "Committing transaction...\n");
4255 0 : qs = postgres_commit (pg);
4256 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
4257 0 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
4258 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4259 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4260 0 : goto RETRY;
4261 0 : return qs;
4262 : }
4263 :
4264 :
4265 : /**
4266 : * Obtain information about wire fees charged by an exchange,
4267 : * including signature (so we have proof).
4268 : *
4269 : * @param cls closure
4270 : * @param master_pub public key of the exchange
4271 : * @param wire_method the wire method
4272 : * @param contract_date date of the contract to use for the lookup
4273 : * @param[out] fees wire fees charged
4274 : * @param[out] start_date start of fee being used
4275 : * @param[out] end_date end of fee being used
4276 : * @param[out] master_sig signature of exchange over fee structure
4277 : * @return transaction status code
4278 : */
4279 : static enum GNUNET_DB_QueryStatus
4280 0 : postgres_lookup_wire_fee (void *cls,
4281 : const struct TALER_MasterPublicKeyP *master_pub,
4282 : const char *wire_method,
4283 : struct GNUNET_TIME_Timestamp contract_date,
4284 : struct TALER_WireFeeSet *fees,
4285 : struct GNUNET_TIME_Timestamp *start_date,
4286 : struct GNUNET_TIME_Timestamp *end_date,
4287 : struct TALER_MasterSignatureP *master_sig)
4288 : {
4289 0 : struct PostgresClosure *pg = cls;
4290 : struct GNUNET_HashCode h_wire_method;
4291 0 : struct GNUNET_PQ_QueryParam params[] = {
4292 0 : GNUNET_PQ_query_param_auto_from_type (master_pub),
4293 0 : GNUNET_PQ_query_param_auto_from_type (&h_wire_method),
4294 0 : GNUNET_PQ_query_param_timestamp (&contract_date),
4295 : GNUNET_PQ_query_param_end
4296 : };
4297 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4298 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
4299 : &fees->wire),
4300 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
4301 : &fees->closing),
4302 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("wad_fee",
4303 : &fees->wad),
4304 0 : GNUNET_PQ_result_spec_timestamp ("start_date",
4305 : start_date),
4306 0 : GNUNET_PQ_result_spec_timestamp ("end_date",
4307 : end_date),
4308 0 : GNUNET_PQ_result_spec_auto_from_type ("master_sig",
4309 : master_sig),
4310 : GNUNET_PQ_result_spec_end
4311 : };
4312 :
4313 0 : check_connection (pg);
4314 0 : GNUNET_CRYPTO_hash (wire_method,
4315 0 : strlen (wire_method) + 1,
4316 : &h_wire_method);
4317 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4318 : "lookup_wire_fee",
4319 : params,
4320 : rs);
4321 : }
4322 :
4323 :
4324 : /**
4325 : * Closure for #lookup_deposits_by_contract_and_coin_cb().
4326 : */
4327 : struct LookupDepositsByCnCContext
4328 : {
4329 : /**
4330 : * Function to call for each deposit.
4331 : */
4332 : TALER_MERCHANTDB_CoinDepositCallback cb;
4333 :
4334 : /**
4335 : * Closure for @e cb.
4336 : */
4337 : void *cb_cls;
4338 :
4339 : /**
4340 : * Plugin context.
4341 : */
4342 : struct PostgresClosure *pg;
4343 :
4344 : /**
4345 : * Transaction result.
4346 : */
4347 : enum GNUNET_DB_QueryStatus qs;
4348 : };
4349 :
4350 :
4351 : /**
4352 : * Function to be called with the results of a SELECT statement
4353 : * that has returned @a num_results results.
4354 : *
4355 : * @param cls of type `struct LookupDepositsByCnCContext *`
4356 : * @param result the postgres result
4357 : * @param num_results the number of results in @a result
4358 : */
4359 : static void
4360 0 : lookup_deposits_by_contract_and_coin_cb (void *cls,
4361 : PGresult *result,
4362 : unsigned int num_results)
4363 : {
4364 0 : struct LookupDepositsByCnCContext *ldcc = cls;
4365 0 : struct PostgresClosure *pg = ldcc->pg;
4366 :
4367 0 : for (unsigned int i = 0; i<num_results; i++)
4368 : {
4369 : char *exchange_url;
4370 : struct TALER_Amount amount_with_fee;
4371 : struct TALER_Amount deposit_fee;
4372 : struct TALER_Amount refund_fee;
4373 : struct TALER_Amount wire_fee;
4374 : struct TALER_MerchantWireHashP h_wire;
4375 : struct GNUNET_TIME_Timestamp deposit_timestamp;
4376 : struct GNUNET_TIME_Timestamp refund_deadline;
4377 : struct TALER_ExchangeSignatureP exchange_sig;
4378 : struct TALER_ExchangePublicKeyP exchange_pub;
4379 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4380 0 : GNUNET_PQ_result_spec_string ("exchange_url",
4381 : &exchange_url),
4382 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
4383 : &amount_with_fee),
4384 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
4385 : &deposit_fee),
4386 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
4387 : &refund_fee),
4388 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
4389 : &wire_fee),
4390 0 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
4391 : &h_wire),
4392 0 : GNUNET_PQ_result_spec_timestamp ("deposit_timestamp",
4393 : &deposit_timestamp),
4394 0 : GNUNET_PQ_result_spec_timestamp ("refund_deadline",
4395 : &refund_deadline),
4396 0 : GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
4397 : &exchange_sig),
4398 0 : GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
4399 : &exchange_pub),
4400 : GNUNET_PQ_result_spec_end
4401 : };
4402 :
4403 0 : if (GNUNET_OK !=
4404 0 : GNUNET_PQ_extract_result (result,
4405 : rs,
4406 : i))
4407 : {
4408 0 : GNUNET_break (0);
4409 0 : ldcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4410 0 : return;
4411 : }
4412 0 : ldcc->cb (ldcc->cb_cls,
4413 : exchange_url,
4414 : &amount_with_fee,
4415 : &deposit_fee,
4416 : &refund_fee,
4417 : &wire_fee,
4418 : &h_wire,
4419 : deposit_timestamp,
4420 : refund_deadline,
4421 : &exchange_sig,
4422 : &exchange_pub);
4423 0 : GNUNET_PQ_cleanup_result (rs);
4424 : }
4425 0 : ldcc->qs = num_results;
4426 : }
4427 :
4428 :
4429 : /**
4430 : * Lookup information about coin payments by @a h_contract_terms and
4431 : * @a coin_pub.
4432 : *
4433 : * @param cls closure
4434 : * @param instance_id instance to lookup payments for
4435 : * @param h_contract_terms proposal data's hashcode
4436 : * @param coin_pub public key to use for the search
4437 : * @param cb function to call with payment data
4438 : * @param cb_cls closure for @a cb
4439 : * @return transaction status
4440 : */
4441 : static enum GNUNET_DB_QueryStatus
4442 0 : postgres_lookup_deposits_by_contract_and_coin (
4443 : void *cls,
4444 : const char *instance_id,
4445 : const struct TALER_PrivateContractHashP *h_contract_terms,
4446 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
4447 : TALER_MERCHANTDB_CoinDepositCallback cb,
4448 : void *cb_cls)
4449 : {
4450 0 : struct PostgresClosure *pg = cls;
4451 0 : struct GNUNET_PQ_QueryParam params[] = {
4452 0 : GNUNET_PQ_query_param_string (instance_id),
4453 0 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
4454 0 : GNUNET_PQ_query_param_auto_from_type (coin_pub),
4455 : GNUNET_PQ_query_param_end
4456 : };
4457 0 : struct LookupDepositsByCnCContext ldcc = {
4458 : .cb = cb,
4459 : .cb_cls = cb_cls,
4460 : .pg = pg
4461 : };
4462 : enum GNUNET_DB_QueryStatus qs;
4463 :
4464 0 : check_connection (pg);
4465 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
4466 : pg->conn,
4467 : "lookup_deposits_by_contract_and_coin",
4468 : params,
4469 : &lookup_deposits_by_contract_and_coin_cb,
4470 : &ldcc);
4471 0 : if (0 >= qs)
4472 0 : return qs;
4473 0 : return ldcc.qs;
4474 : }
4475 :
4476 :
4477 : /**
4478 : * Lookup transfer status.
4479 : *
4480 : * @param cls closure
4481 : * @param instance_id at which instance should we resolve the transfer
4482 : * @param exchange_url the exchange that made the transfer
4483 : * @param wtid wire transfer subject
4484 : * @param[out] total_amount amount that was debited from our
4485 : * aggregate balance at the exchange (in total, sum of
4486 : * the wire transfer amount and the @a wire_fee)
4487 : * @param[out] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true)
4488 : * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true)
4489 : * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true)
4490 : * @param[out] have_exchange_sig do we have a response from the exchange about this transfer
4491 : * @param[out] verified did we confirm the transfer was OK
4492 : * @return transaction status
4493 : */
4494 : static enum GNUNET_DB_QueryStatus
4495 0 : postgres_lookup_transfer (
4496 : void *cls,
4497 : const char *instance_id,
4498 : const char *exchange_url,
4499 : const struct TALER_WireTransferIdentifierRawP *wtid,
4500 : struct TALER_Amount *total_amount,
4501 : struct TALER_Amount *wire_fee,
4502 : struct TALER_Amount *exchange_amount,
4503 : struct GNUNET_TIME_Timestamp *execution_time,
4504 : bool *have_exchange_sig,
4505 : bool *verified)
4506 : {
4507 0 : struct PostgresClosure *pg = cls;
4508 0 : struct GNUNET_PQ_QueryParam params[] = {
4509 0 : GNUNET_PQ_query_param_string (exchange_url),
4510 0 : GNUNET_PQ_query_param_auto_from_type (wtid),
4511 0 : GNUNET_PQ_query_param_string (instance_id),
4512 : GNUNET_PQ_query_param_end
4513 : };
4514 : uint8_t verified8;
4515 : /** Amount we got actually credited, _excludes_ the wire fee */
4516 : bool no_sig;
4517 : struct TALER_Amount credit_amount;
4518 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4519 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
4520 : &credit_amount),
4521 0 : GNUNET_PQ_result_spec_allow_null (
4522 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
4523 : wire_fee),
4524 : &no_sig),
4525 0 : GNUNET_PQ_result_spec_allow_null (
4526 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount",
4527 : exchange_amount),
4528 : NULL),
4529 0 : GNUNET_PQ_result_spec_allow_null (
4530 : GNUNET_PQ_result_spec_timestamp ("execution_time",
4531 : execution_time),
4532 : NULL),
4533 0 : GNUNET_PQ_result_spec_auto_from_type ("verified",
4534 : &verified8),
4535 : GNUNET_PQ_result_spec_end
4536 : };
4537 : enum GNUNET_DB_QueryStatus qs;
4538 :
4539 0 : check_connection (pg);
4540 0 : *execution_time = GNUNET_TIME_UNIT_ZERO_TS;
4541 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4542 : "lookup_transfer",
4543 : params,
4544 : rs);
4545 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4546 : "Lookup transfer returned %d\n",
4547 : qs);
4548 0 : if (qs > 0)
4549 : {
4550 0 : *have_exchange_sig = ! no_sig;
4551 0 : *verified = (0 != verified8);
4552 0 : if ( (! no_sig) &&
4553 : (0 >
4554 0 : TALER_amount_add (total_amount,
4555 : &credit_amount,
4556 : wire_fee)) )
4557 : {
4558 0 : GNUNET_break (0);
4559 0 : return GNUNET_DB_STATUS_HARD_ERROR;
4560 : }
4561 : }
4562 : else
4563 : {
4564 0 : *verified = false;
4565 0 : *have_exchange_sig = false;
4566 : }
4567 0 : return qs;
4568 : }
4569 :
4570 :
4571 : /**
4572 : * Set transfer status to verified.
4573 : *
4574 : * @param cls closure
4575 : * @param exchange_url the exchange that made the transfer
4576 : * @param wtid wire transfer subject
4577 : * @return transaction status
4578 : */
4579 : static enum GNUNET_DB_QueryStatus
4580 0 : postgres_set_transfer_status_to_verified (
4581 : void *cls,
4582 : const char *exchange_url,
4583 : const struct TALER_WireTransferIdentifierRawP *wtid)
4584 : {
4585 0 : struct PostgresClosure *pg = cls;
4586 0 : struct GNUNET_PQ_QueryParam params[] = {
4587 0 : GNUNET_PQ_query_param_auto_from_type (wtid),
4588 0 : GNUNET_PQ_query_param_string (exchange_url),
4589 : GNUNET_PQ_query_param_end
4590 : };
4591 :
4592 0 : check_connection (pg);
4593 0 : return GNUNET_PQ_eval_prepared_non_select (
4594 : pg->conn,
4595 : "set_transfer_status_to_verified",
4596 : params);
4597 : }
4598 :
4599 :
4600 : /**
4601 : * Closure for #lookup_transfer_summary_cb().
4602 : */
4603 : struct LookupTransferSummaryContext
4604 : {
4605 : /**
4606 : * Function to call for each order that was aggregated.
4607 : */
4608 : TALER_MERCHANTDB_TransferSummaryCallback cb;
4609 :
4610 : /**
4611 : * Closure for @e cb.
4612 : */
4613 : void *cb_cls;
4614 :
4615 : /**
4616 : * Plugin context.
4617 : */
4618 : struct PostgresClosure *pg;
4619 :
4620 : /**
4621 : * Transaction result.
4622 : */
4623 : enum GNUNET_DB_QueryStatus qs;
4624 : };
4625 :
4626 :
4627 : /**
4628 : * Function to be called with the results of a SELECT statement
4629 : * that has returned @a num_results results.
4630 : *
4631 : * @param cls of type `struct LookupTransferSummaryContext *`
4632 : * @param result the postgres result
4633 : * @param num_results the number of results in @a result
4634 : */
4635 : static void
4636 0 : lookup_transfer_summary_cb (void *cls,
4637 : PGresult *result,
4638 : unsigned int num_results)
4639 : {
4640 0 : struct LookupTransferSummaryContext *ltdc = cls;
4641 0 : struct PostgresClosure *pg = ltdc->pg;
4642 :
4643 0 : for (unsigned int i = 0; i<num_results; i++)
4644 : {
4645 : char *order_id;
4646 : struct TALER_Amount deposit_value;
4647 : struct TALER_Amount deposit_fee;
4648 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4649 0 : GNUNET_PQ_result_spec_string ("order_id",
4650 : &order_id),
4651 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
4652 : &deposit_value),
4653 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
4654 : &deposit_fee),
4655 : GNUNET_PQ_result_spec_end
4656 : };
4657 :
4658 0 : if (GNUNET_OK !=
4659 0 : GNUNET_PQ_extract_result (result,
4660 : rs,
4661 : i))
4662 : {
4663 0 : GNUNET_break (0);
4664 0 : ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4665 0 : return;
4666 : }
4667 0 : ltdc->cb (ltdc->cb_cls,
4668 : order_id,
4669 : &deposit_value,
4670 : &deposit_fee);
4671 0 : GNUNET_PQ_cleanup_result (rs);
4672 : }
4673 0 : ltdc->qs = num_results;
4674 : }
4675 :
4676 :
4677 : /**
4678 : * Lookup transfer summary.
4679 : *
4680 : * @param cls closure
4681 : * @param exchange_url the exchange that made the transfer
4682 : * @param wtid wire transfer subject
4683 : * @param cb function to call with detailed transfer data
4684 : * @param cb_cls closure for @a cb
4685 : * @return transaction status
4686 : */
4687 : static enum GNUNET_DB_QueryStatus
4688 0 : postgres_lookup_transfer_summary (
4689 : void *cls,
4690 : const char *exchange_url,
4691 : const struct TALER_WireTransferIdentifierRawP *wtid,
4692 : TALER_MERCHANTDB_TransferSummaryCallback cb,
4693 : void *cb_cls)
4694 : {
4695 0 : struct PostgresClosure *pg = cls;
4696 0 : struct GNUNET_PQ_QueryParam params[] = {
4697 0 : GNUNET_PQ_query_param_string (exchange_url),
4698 0 : GNUNET_PQ_query_param_auto_from_type (wtid),
4699 : GNUNET_PQ_query_param_end
4700 : };
4701 0 : struct LookupTransferSummaryContext ltdc = {
4702 : .cb = cb,
4703 : .cb_cls = cb_cls,
4704 : .pg = pg
4705 : };
4706 : enum GNUNET_DB_QueryStatus qs;
4707 :
4708 0 : check_connection (pg);
4709 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
4710 : pg->conn,
4711 : "lookup_transfer_summary",
4712 : params,
4713 : &lookup_transfer_summary_cb,
4714 : <dc);
4715 0 : if (0 >= qs)
4716 0 : return qs;
4717 0 : return ltdc.qs;
4718 : }
4719 :
4720 :
4721 : /**
4722 : * Closure for #lookup_transfer_details_cb().
4723 : */
4724 : struct LookupTransferDetailsContext
4725 : {
4726 : /**
4727 : * Function to call for each order that was aggregated.
4728 : */
4729 : TALER_MERCHANTDB_TransferDetailsCallback cb;
4730 :
4731 : /**
4732 : * Closure for @e cb.
4733 : */
4734 : void *cb_cls;
4735 :
4736 : /**
4737 : * Plugin context.
4738 : */
4739 : struct PostgresClosure *pg;
4740 :
4741 : /**
4742 : * Transaction result.
4743 : */
4744 : enum GNUNET_DB_QueryStatus qs;
4745 : };
4746 :
4747 :
4748 : /**
4749 : * Function to be called with the results of a SELECT statement
4750 : * that has returned @a num_results results.
4751 : *
4752 : * @param cls of type `struct LookupTransferDetailsContext *`
4753 : * @param result the postgres result
4754 : * @param num_results the number of results in @a result
4755 : */
4756 : static void
4757 0 : lookup_transfer_details_cb (void *cls,
4758 : PGresult *result,
4759 : unsigned int num_results)
4760 : {
4761 0 : struct LookupTransferDetailsContext *ltdc = cls;
4762 0 : struct PostgresClosure *pg = ltdc->pg;
4763 :
4764 0 : for (unsigned int i = 0; i<num_results; i++)
4765 : {
4766 : uint64_t current_offset;
4767 : struct TALER_TrackTransferDetails ttd;
4768 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4769 0 : GNUNET_PQ_result_spec_uint64 ("offset_in_exchange_list",
4770 : ¤t_offset),
4771 0 : GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
4772 : &ttd.h_contract_terms),
4773 0 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
4774 : &ttd.coin_pub),
4775 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
4776 : &ttd.coin_value),
4777 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
4778 : &ttd.coin_fee),
4779 : GNUNET_PQ_result_spec_end
4780 : };
4781 :
4782 0 : if (GNUNET_OK !=
4783 0 : GNUNET_PQ_extract_result (result,
4784 : rs,
4785 : i))
4786 : {
4787 0 : GNUNET_break (0);
4788 0 : ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4789 0 : return;
4790 : }
4791 0 : ltdc->cb (ltdc->cb_cls,
4792 : (unsigned int) current_offset,
4793 : &ttd);
4794 0 : GNUNET_PQ_cleanup_result (rs);
4795 : }
4796 0 : ltdc->qs = num_results;
4797 : }
4798 :
4799 :
4800 : /**
4801 : * Lookup transfer details.
4802 : *
4803 : * @param cls closure
4804 : * @param exchange_url the exchange that made the transfer
4805 : * @param wtid wire transfer subject
4806 : * @param cb function to call with detailed transfer data
4807 : * @param cb_cls closure for @a cb
4808 : * @return transaction status
4809 : */
4810 : static enum GNUNET_DB_QueryStatus
4811 0 : postgres_lookup_transfer_details (
4812 : void *cls,
4813 : const char *exchange_url,
4814 : const struct TALER_WireTransferIdentifierRawP *wtid,
4815 : TALER_MERCHANTDB_TransferDetailsCallback cb,
4816 : void *cb_cls)
4817 : {
4818 0 : struct PostgresClosure *pg = cls;
4819 0 : struct GNUNET_PQ_QueryParam params[] = {
4820 0 : GNUNET_PQ_query_param_string (exchange_url),
4821 0 : GNUNET_PQ_query_param_auto_from_type (wtid),
4822 : GNUNET_PQ_query_param_end
4823 : };
4824 0 : struct LookupTransferDetailsContext ltdc = {
4825 : .cb = cb,
4826 : .cb_cls = cb_cls,
4827 : .pg = pg
4828 : };
4829 : enum GNUNET_DB_QueryStatus qs;
4830 :
4831 0 : check_connection (pg);
4832 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
4833 : pg->conn,
4834 : "lookup_transfer_details",
4835 : params,
4836 : &lookup_transfer_details_cb,
4837 : <dc);
4838 0 : if (0 >= qs)
4839 0 : return qs;
4840 0 : return ltdc.qs;
4841 : }
4842 :
4843 :
4844 : /**
4845 : * Closure for #lookup_transfers_cb().
4846 : */
4847 : struct LookupTransfersContext
4848 : {
4849 : /**
4850 : * Function to call on results.
4851 : */
4852 : TALER_MERCHANTDB_TransferCallback cb;
4853 :
4854 : /**
4855 : * Closure for @e cb.
4856 : */
4857 : void *cb_cls;
4858 :
4859 : /**
4860 : * Postgres context.
4861 : */
4862 : struct PostgresClosure *pg;
4863 :
4864 : /**
4865 : * Transaction status (set).
4866 : */
4867 : enum GNUNET_DB_QueryStatus qs;
4868 :
4869 : /**
4870 : * Filter to apply by verification status.
4871 : */
4872 : enum TALER_EXCHANGE_YesNoAll verified;
4873 : };
4874 :
4875 :
4876 : /**
4877 : * Function to be called with the results of a SELECT statement
4878 : * that has returned @a num_results results.
4879 : *
4880 : * @param cls of type `struct LookupTransfersContext *`
4881 : * @param result the postgres result
4882 : * @param num_results the number of results in @a result
4883 : */
4884 : static void
4885 0 : lookup_transfers_cb (void *cls,
4886 : PGresult *result,
4887 : unsigned int num_results)
4888 : {
4889 0 : struct LookupTransfersContext *ltc = cls;
4890 0 : struct PostgresClosure *pg = ltc->pg;
4891 :
4892 0 : for (unsigned int i = 0; i<num_results; i++)
4893 : {
4894 : struct TALER_Amount credit_amount;
4895 : struct TALER_WireTransferIdentifierRawP wtid;
4896 : char *payto_uri;
4897 : char *exchange_url;
4898 : uint64_t transfer_serial_id;
4899 : struct GNUNET_TIME_Timestamp execution_time;
4900 : enum TALER_EXCHANGE_YesNoAll verified;
4901 : uint8_t verified8;
4902 : uint8_t confirmed8;
4903 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4904 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
4905 : &credit_amount),
4906 0 : GNUNET_PQ_result_spec_auto_from_type ("wtid",
4907 : &wtid),
4908 0 : GNUNET_PQ_result_spec_string ("payto_uri",
4909 : &payto_uri),
4910 0 : GNUNET_PQ_result_spec_string ("exchange_url",
4911 : &exchange_url),
4912 0 : GNUNET_PQ_result_spec_uint64 ("credit_serial",
4913 : &transfer_serial_id),
4914 0 : GNUNET_PQ_result_spec_timestamp ("execution_time",
4915 : &execution_time),
4916 0 : GNUNET_PQ_result_spec_auto_from_type ("verified",
4917 : &verified8),
4918 0 : GNUNET_PQ_result_spec_auto_from_type ("confirmed",
4919 : &confirmed8),
4920 : GNUNET_PQ_result_spec_end
4921 : };
4922 :
4923 0 : if (GNUNET_OK !=
4924 0 : GNUNET_PQ_extract_result (result,
4925 : rs,
4926 : i))
4927 : {
4928 0 : GNUNET_break (0);
4929 0 : ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4930 0 : return;
4931 : }
4932 0 : if (0 == verified8)
4933 0 : verified = TALER_EXCHANGE_YNA_NO;
4934 : else
4935 0 : verified = TALER_EXCHANGE_YNA_YES;
4936 0 : if ( (ltc->verified == TALER_EXCHANGE_YNA_ALL) ||
4937 0 : (ltc->verified == verified) )
4938 : {
4939 0 : ltc->cb (ltc->cb_cls,
4940 : &credit_amount,
4941 : &wtid,
4942 : payto_uri,
4943 : exchange_url,
4944 : transfer_serial_id,
4945 : execution_time,
4946 : TALER_EXCHANGE_YNA_YES == verified,
4947 : 0 != confirmed8);
4948 : }
4949 0 : GNUNET_PQ_cleanup_result (rs);
4950 : }
4951 0 : ltc->qs = num_results;
4952 : }
4953 :
4954 :
4955 : /**
4956 : * Lookup transfers. Note that filtering by @a verified status is done
4957 : * outside of SQL, as we already have 8 prepared statements and adding
4958 : * a filter on verified would further double the number of statements for
4959 : * a likely rather ineffective filter. So we apply that filter in
4960 : * #lookup_transfers_cb().
4961 : *
4962 : * @param cls closure
4963 : * @param instance_id instance to lookup payments for
4964 : * @param payto_uri account that we are interested in transfers to
4965 : * @param before timestamp for the earliest transfer we care about
4966 : * @param after timestamp for the last transfer we care about
4967 : * @param limit number of entries to return, negative for descending in execution time,
4968 : * positive for ascending in execution time
4969 : * @param offset transfer_serial number of the transfer we want to offset from
4970 : * @param verified filter transfers by verification status
4971 : * @param cb function to call with detailed transfer data
4972 : * @param cb_cls closure for @a cb
4973 : * @return transaction status
4974 : */
4975 : static enum GNUNET_DB_QueryStatus
4976 0 : postgres_lookup_transfers (void *cls,
4977 : const char *instance_id,
4978 : const char *payto_uri,
4979 : struct GNUNET_TIME_Timestamp before,
4980 : struct GNUNET_TIME_Timestamp after,
4981 : int64_t limit,
4982 : uint64_t offset,
4983 : enum TALER_EXCHANGE_YesNoAll verified,
4984 : TALER_MERCHANTDB_TransferCallback cb,
4985 : void *cb_cls)
4986 : {
4987 0 : struct PostgresClosure *pg = cls;
4988 0 : uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
4989 0 : struct LookupTransfersContext ltc = {
4990 : .cb = cb,
4991 : .cb_cls = cb_cls,
4992 : .pg = pg,
4993 : .verified = verified
4994 : };
4995 : enum GNUNET_DB_QueryStatus qs;
4996 : bool by_time;
4997 :
4998 0 : by_time = ( (! GNUNET_TIME_absolute_is_never (before.abs_time)) ||
4999 0 : (! GNUNET_TIME_absolute_is_zero (after.abs_time)) );
5000 0 : check_connection (pg);
5001 0 : if (by_time)
5002 : {
5003 0 : if (NULL != payto_uri)
5004 : {
5005 0 : struct GNUNET_PQ_QueryParam params[] = {
5006 0 : GNUNET_PQ_query_param_string (instance_id),
5007 0 : GNUNET_PQ_query_param_timestamp (&before),
5008 0 : GNUNET_PQ_query_param_timestamp (&after),
5009 0 : GNUNET_PQ_query_param_uint64 (&offset),
5010 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5011 0 : GNUNET_PQ_query_param_string (payto_uri),
5012 : GNUNET_PQ_query_param_end
5013 : };
5014 :
5015 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5016 : pg->conn,
5017 : (limit > 0)
5018 : ? "lookup_transfers_time_payto_asc"
5019 : : "lookup_transfers_time_payto_desc",
5020 : params,
5021 : &lookup_transfers_cb,
5022 : <c);
5023 : }
5024 : else
5025 : {
5026 0 : struct GNUNET_PQ_QueryParam params[] = {
5027 0 : GNUNET_PQ_query_param_string (instance_id),
5028 0 : GNUNET_PQ_query_param_timestamp (&before),
5029 0 : GNUNET_PQ_query_param_timestamp (&after),
5030 0 : GNUNET_PQ_query_param_uint64 (&offset),
5031 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5032 : GNUNET_PQ_query_param_end
5033 : };
5034 :
5035 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5036 : pg->conn,
5037 : (limit > 0)
5038 : ? "lookup_transfers_time_asc"
5039 : : "lookup_transfers_time_desc",
5040 : params,
5041 : &lookup_transfers_cb,
5042 : <c);
5043 : }
5044 : }
5045 : else
5046 : {
5047 0 : if (NULL != payto_uri)
5048 : {
5049 0 : struct GNUNET_PQ_QueryParam params[] = {
5050 0 : GNUNET_PQ_query_param_string (instance_id),
5051 0 : GNUNET_PQ_query_param_uint64 (&offset),
5052 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5053 0 : GNUNET_PQ_query_param_string (payto_uri),
5054 : GNUNET_PQ_query_param_end
5055 : };
5056 :
5057 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5058 : pg->conn,
5059 : (limit > 0)
5060 : ? "lookup_transfers_payto_asc"
5061 : : "lookup_transfers_payto_desc",
5062 : params,
5063 : &lookup_transfers_cb,
5064 : <c);
5065 : }
5066 : else
5067 : {
5068 0 : struct GNUNET_PQ_QueryParam params[] = {
5069 0 : GNUNET_PQ_query_param_string (instance_id),
5070 0 : GNUNET_PQ_query_param_uint64 (&offset),
5071 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5072 : GNUNET_PQ_query_param_end
5073 : };
5074 :
5075 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5076 : pg->conn,
5077 : (limit > 0)
5078 : ? "lookup_transfers_asc"
5079 : : "lookup_transfers_desc",
5080 : params,
5081 : &lookup_transfers_cb,
5082 : <c);
5083 : }
5084 : }
5085 0 : if (0 >= qs)
5086 0 : return qs;
5087 0 : return ltc.qs;
5088 : }
5089 :
5090 :
5091 : /**
5092 : * Store information about wire fees charged by an exchange,
5093 : * including signature (so we have proof).
5094 : *
5095 : * @param cls closure
5096 : * @param master_pub public key of the exchange
5097 : * @param h_wire_method hash of wire method
5098 : * @param fees the fee charged
5099 : * @param start_date start of fee being used
5100 : * @param end_date end of fee being used
5101 : * @param master_sig signature of exchange over fee structure
5102 : * @return transaction status code
5103 : */
5104 : static enum GNUNET_DB_QueryStatus
5105 0 : postgres_store_wire_fee_by_exchange (
5106 : void *cls,
5107 : const struct TALER_MasterPublicKeyP *master_pub,
5108 : const struct GNUNET_HashCode *h_wire_method,
5109 : const struct TALER_WireFeeSet *fees,
5110 : struct GNUNET_TIME_Timestamp start_date,
5111 : struct GNUNET_TIME_Timestamp end_date,
5112 : const struct TALER_MasterSignatureP *master_sig)
5113 : {
5114 0 : struct PostgresClosure *pg = cls;
5115 0 : struct GNUNET_PQ_QueryParam params[] = {
5116 0 : GNUNET_PQ_query_param_auto_from_type (master_pub),
5117 0 : GNUNET_PQ_query_param_auto_from_type (h_wire_method),
5118 0 : TALER_PQ_query_param_amount (&fees->wire),
5119 0 : TALER_PQ_query_param_amount (&fees->closing),
5120 0 : TALER_PQ_query_param_amount (&fees->wad),
5121 0 : GNUNET_PQ_query_param_timestamp (&start_date),
5122 0 : GNUNET_PQ_query_param_timestamp (&end_date),
5123 0 : GNUNET_PQ_query_param_auto_from_type (master_sig),
5124 : GNUNET_PQ_query_param_end
5125 : };
5126 :
5127 : /* no preflight check here, run in its own transaction by the caller */
5128 0 : check_connection (pg);
5129 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5130 : "Storing wire fee for %s starting at %s of %s\n",
5131 : TALER_B2S (master_pub),
5132 : GNUNET_TIME_timestamp2s (start_date),
5133 : TALER_amount2s (&fees->wire));
5134 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5135 : "insert_wire_fee",
5136 : params);
5137 : }
5138 :
5139 :
5140 : /**
5141 : * Add @a credit to a reserve to be used for tipping. Note that
5142 : * this function does not actually perform any wire transfers to
5143 : * credit the reserve, it merely tells the merchant backend that
5144 : * a reserve now exists. This has to happen before tips can be
5145 : * authorized.
5146 : *
5147 : * @param cls closure, typically a connection to the db
5148 : * @param instance_id which instance is the reserve tied to
5149 : * @param reserve_priv which reserve is topped up or created
5150 : * @param reserve_pub which reserve is topped up or created
5151 : * @param exchange_url what URL is the exchange reachable at where the reserve is located
5152 : * @param payto_uri URI to use to fund the reserve
5153 : * @param initial_balance how much money will be added to the reserve
5154 : * @param expiration when does the reserve expire?
5155 : * @return transaction status, usually
5156 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5157 : */
5158 : static enum TALER_ErrorCode
5159 0 : postgres_insert_reserve (void *cls,
5160 : const char *instance_id,
5161 : const struct TALER_ReservePrivateKeyP *reserve_priv,
5162 : const struct TALER_ReservePublicKeyP *reserve_pub,
5163 : const char *exchange_url,
5164 : const char *payto_uri,
5165 : const struct TALER_Amount *initial_balance,
5166 : struct GNUNET_TIME_Timestamp expiration)
5167 : {
5168 0 : struct PostgresClosure *pg = cls;
5169 : unsigned int retries;
5170 : enum GNUNET_DB_QueryStatus qs;
5171 :
5172 0 : retries = 0;
5173 0 : check_connection (pg);
5174 0 : RETRY:
5175 0 : if (MAX_RETRIES < ++retries)
5176 0 : return TALER_EC_GENERIC_DB_SOFT_FAILURE;
5177 0 : if (GNUNET_OK !=
5178 0 : postgres_start (pg,
5179 : "insert reserve"))
5180 : {
5181 0 : GNUNET_break (0);
5182 0 : return TALER_EC_GENERIC_DB_START_FAILED;
5183 : }
5184 :
5185 : /* Setup reserve */
5186 : {
5187 : struct GNUNET_TIME_Timestamp now;
5188 0 : struct GNUNET_PQ_QueryParam params[] = {
5189 0 : GNUNET_PQ_query_param_string (instance_id),
5190 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5191 0 : GNUNET_PQ_query_param_timestamp (&now),
5192 0 : GNUNET_PQ_query_param_timestamp (&expiration),
5193 0 : TALER_PQ_query_param_amount (initial_balance),
5194 : GNUNET_PQ_query_param_end
5195 : };
5196 :
5197 0 : now = GNUNET_TIME_timestamp_get ();
5198 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
5199 : "insert_reserve",
5200 : params);
5201 0 : if (0 > qs)
5202 : {
5203 0 : postgres_rollback (pg);
5204 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
5205 0 : goto RETRY;
5206 0 : return qs;
5207 : }
5208 : }
5209 : /* Store private key */
5210 : {
5211 0 : struct GNUNET_PQ_QueryParam params[] = {
5212 0 : GNUNET_PQ_query_param_string (instance_id),
5213 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5214 0 : GNUNET_PQ_query_param_auto_from_type (reserve_priv),
5215 0 : GNUNET_PQ_query_param_string (exchange_url),
5216 0 : GNUNET_PQ_query_param_string (payto_uri),
5217 : GNUNET_PQ_query_param_end
5218 : };
5219 :
5220 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
5221 : "insert_reserve_key",
5222 : params);
5223 0 : if (0 > qs)
5224 : {
5225 0 : postgres_rollback (pg);
5226 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
5227 0 : goto RETRY;
5228 0 : return qs;
5229 : }
5230 : }
5231 0 : qs = postgres_commit (pg);
5232 0 : if (0 <= qs)
5233 0 : return TALER_EC_NONE; /* success */
5234 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
5235 0 : goto RETRY;
5236 0 : return qs;
5237 : }
5238 :
5239 :
5240 : /**
5241 : * Confirms @a credit as the amount the exchange claims to have received and
5242 : * thus really 'activates' the reserve. This has to happen before tips can
5243 : * be authorized.
5244 : *
5245 : * @param cls closure, typically a connection to the db
5246 : * @param instance_id which instance is the reserve tied to
5247 : * @param reserve_pub which reserve is topped up or created
5248 : * @param initial_exchange_balance how much money was be added to the reserve
5249 : * according to the exchange
5250 : * @return transaction status, usually
5251 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5252 : */
5253 : static enum GNUNET_DB_QueryStatus
5254 0 : postgres_activate_reserve (void *cls,
5255 : const char *instance_id,
5256 : const struct TALER_ReservePublicKeyP *reserve_pub,
5257 : const struct TALER_Amount *initial_exchange_balance)
5258 : {
5259 0 : struct PostgresClosure *pg = cls;
5260 0 : struct GNUNET_PQ_QueryParam params[] = {
5261 0 : GNUNET_PQ_query_param_string (instance_id),
5262 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5263 0 : TALER_PQ_query_param_amount (initial_exchange_balance),
5264 : GNUNET_PQ_query_param_end
5265 : };
5266 :
5267 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5268 : "activate_reserve",
5269 : params);
5270 : }
5271 :
5272 :
5273 : /**
5274 : * Closure for #lookup_reserves_cb.
5275 : */
5276 : struct LookupReservesContext
5277 : {
5278 : /**
5279 : * Postgres context.
5280 : */
5281 : struct PostgresClosure *pg;
5282 :
5283 : /**
5284 : * Function to call with the results
5285 : */
5286 : TALER_MERCHANTDB_ReservesCallback cb;
5287 :
5288 : /**
5289 : * Closure for @e cb
5290 : */
5291 : void *cb_cls;
5292 :
5293 : /**
5294 : * Filter by active reserves.
5295 : */
5296 : enum TALER_EXCHANGE_YesNoAll active;
5297 :
5298 : /**
5299 : * Filter by failures (mismatch in exchange claimed and
5300 : * merchant claimed initial amounts).
5301 : */
5302 : enum TALER_EXCHANGE_YesNoAll failures;
5303 :
5304 : /**
5305 : * Set in case of errors.
5306 : */
5307 : enum GNUNET_DB_QueryStatus qs;
5308 :
5309 : };
5310 :
5311 :
5312 : /**
5313 : * Function to be called with the results of a SELECT statement
5314 : * that has returned @a num_results results about accounts.
5315 : *
5316 : * @param[in,out] cls of type `struct LookupReservesContext *`
5317 : * @param result the postgres result
5318 : * @param num_results the number of results in @a result
5319 : */
5320 : static void
5321 0 : lookup_reserves_cb (void *cls,
5322 : PGresult *result,
5323 : unsigned int num_results)
5324 : {
5325 0 : struct LookupReservesContext *lrc = cls;
5326 0 : struct PostgresClosure *pg = lrc->pg;
5327 :
5328 0 : for (unsigned int i = 0; i < num_results; i++)
5329 : {
5330 : struct TALER_ReservePublicKeyP reserve_pub;
5331 : struct GNUNET_TIME_Timestamp creation_time;
5332 : struct GNUNET_TIME_Timestamp expiration_time;
5333 : struct TALER_Amount merchant_initial_balance;
5334 : struct TALER_Amount exchange_initial_balance;
5335 : struct TALER_Amount pickup_amount;
5336 : struct TALER_Amount committed_amount;
5337 : uint8_t active;
5338 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5339 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
5340 : &reserve_pub),
5341 0 : GNUNET_PQ_result_spec_timestamp ("creation_time",
5342 : &creation_time),
5343 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
5344 : &expiration_time),
5345 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
5346 : &merchant_initial_balance),
5347 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
5348 : &exchange_initial_balance),
5349 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
5350 : &committed_amount),
5351 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
5352 : &pickup_amount),
5353 0 : GNUNET_PQ_result_spec_auto_from_type ("active",
5354 : &active),
5355 : GNUNET_PQ_result_spec_end
5356 : };
5357 :
5358 0 : if (GNUNET_OK !=
5359 0 : GNUNET_PQ_extract_result (result,
5360 : rs,
5361 : i))
5362 : {
5363 0 : GNUNET_break (0);
5364 0 : lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
5365 0 : return;
5366 : }
5367 0 : switch (lrc->active)
5368 : {
5369 0 : case TALER_EXCHANGE_YNA_YES:
5370 0 : if (0 == active)
5371 0 : continue;
5372 0 : break;
5373 0 : case TALER_EXCHANGE_YNA_NO:
5374 0 : if (0 != active)
5375 0 : continue;
5376 0 : break;
5377 0 : case TALER_EXCHANGE_YNA_ALL:
5378 0 : break;
5379 : }
5380 0 : switch (lrc->failures)
5381 : {
5382 0 : case TALER_EXCHANGE_YNA_YES:
5383 0 : if (0 ==
5384 0 : TALER_amount_cmp (&merchant_initial_balance,
5385 : &exchange_initial_balance))
5386 0 : continue;
5387 0 : break;
5388 0 : case TALER_EXCHANGE_YNA_NO:
5389 0 : if (0 !=
5390 0 : TALER_amount_cmp (&merchant_initial_balance,
5391 : &exchange_initial_balance))
5392 0 : continue;
5393 0 : break;
5394 0 : case TALER_EXCHANGE_YNA_ALL:
5395 0 : break;
5396 : }
5397 0 : lrc->cb (lrc->cb_cls,
5398 : &reserve_pub,
5399 : creation_time,
5400 : expiration_time,
5401 : &merchant_initial_balance,
5402 : &exchange_initial_balance,
5403 : &pickup_amount,
5404 : &committed_amount,
5405 : (0 != active));
5406 : }
5407 : }
5408 :
5409 :
5410 : /**
5411 : * Lookup reserves.
5412 : *
5413 : * @param cls closure
5414 : * @param instance_id instance to lookup payments for
5415 : * @param created_after filter by reserves created after this date
5416 : * @param active filter by active reserves
5417 : * @param failures filter by reserves with a disagreement on the initial balance
5418 : * @param cb function to call with reserve summary data
5419 : * @param cb_cls closure for @a cb
5420 : * @return transaction status
5421 : */
5422 : static enum GNUNET_DB_QueryStatus
5423 0 : postgres_lookup_reserves (void *cls,
5424 : const char *instance_id,
5425 : struct GNUNET_TIME_Timestamp created_after,
5426 : enum TALER_EXCHANGE_YesNoAll active,
5427 : enum TALER_EXCHANGE_YesNoAll failures,
5428 : TALER_MERCHANTDB_ReservesCallback cb,
5429 : void *cb_cls)
5430 : {
5431 0 : struct PostgresClosure *pg = cls;
5432 0 : struct LookupReservesContext lrc = {
5433 : .pg = pg,
5434 : .active = active,
5435 : .failures = failures,
5436 : .cb = cb,
5437 : .cb_cls = cb_cls
5438 : };
5439 0 : struct GNUNET_PQ_QueryParam params[] = {
5440 0 : GNUNET_PQ_query_param_string (instance_id),
5441 0 : GNUNET_PQ_query_param_timestamp (&created_after),
5442 : GNUNET_PQ_query_param_end
5443 : };
5444 : enum GNUNET_DB_QueryStatus qs;
5445 :
5446 0 : check_connection (pg);
5447 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
5448 : "lookup_reserves",
5449 : params,
5450 : &lookup_reserves_cb,
5451 : &lrc);
5452 0 : if (lrc.qs < 0)
5453 0 : return lrc.qs;
5454 0 : return qs;
5455 : }
5456 :
5457 :
5458 : /**
5459 : * Closure for #lookup_pending_reserves_cb.
5460 : */
5461 : struct LookupPendingReservesContext
5462 : {
5463 : /**
5464 : * Postgres context.
5465 : */
5466 : struct PostgresClosure *pg;
5467 :
5468 : /**
5469 : * Function to call with the results
5470 : */
5471 : TALER_MERCHANTDB_PendingReservesCallback cb;
5472 :
5473 : /**
5474 : * Closure for @e cb
5475 : */
5476 : void *cb_cls;
5477 :
5478 : /**
5479 : * Set in case of errors.
5480 : */
5481 : enum GNUNET_DB_QueryStatus qs;
5482 :
5483 : };
5484 :
5485 :
5486 : /**
5487 : * Function to be called with the results of a SELECT statement
5488 : * that has returned @a num_results results about accounts.
5489 : *
5490 : * @param[in,out] cls of type `struct LookupReservesContext *`
5491 : * @param result the postgres result
5492 : * @param num_results the number of results in @a result
5493 : */
5494 : static void
5495 0 : lookup_pending_reserves_cb (void *cls,
5496 : PGresult *result,
5497 : unsigned int num_results)
5498 : {
5499 0 : struct LookupPendingReservesContext *lrc = cls;
5500 0 : struct PostgresClosure *pg = lrc->pg;
5501 :
5502 0 : for (unsigned int i = 0; i < num_results; i++)
5503 : {
5504 : struct TALER_ReservePublicKeyP reserve_pub;
5505 : struct TALER_Amount merchant_initial_balance;
5506 : char *exchange_url;
5507 : char *instance_id;
5508 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5509 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
5510 : &reserve_pub),
5511 0 : GNUNET_PQ_result_spec_string ("merchant_id",
5512 : &instance_id),
5513 0 : GNUNET_PQ_result_spec_string ("exchange_url",
5514 : &exchange_url),
5515 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
5516 : &merchant_initial_balance),
5517 : GNUNET_PQ_result_spec_end
5518 : };
5519 :
5520 0 : if (GNUNET_OK !=
5521 0 : GNUNET_PQ_extract_result (result,
5522 : rs,
5523 : i))
5524 : {
5525 0 : GNUNET_break (0);
5526 0 : lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
5527 0 : return;
5528 : }
5529 0 : lrc->cb (lrc->cb_cls,
5530 : instance_id,
5531 : exchange_url,
5532 : &reserve_pub,
5533 : &merchant_initial_balance);
5534 0 : GNUNET_PQ_cleanup_result (rs);
5535 : }
5536 : }
5537 :
5538 :
5539 : /**
5540 : * Lookup reserves pending activation across all instances.
5541 : *
5542 : * @param cls closure
5543 : * @param cb function to call with reserve summary data
5544 : * @param cb_cls closure for @a cb
5545 : * @return transaction status
5546 : */
5547 : static enum GNUNET_DB_QueryStatus
5548 0 : postgres_lookup_pending_reserves (void *cls,
5549 : TALER_MERCHANTDB_PendingReservesCallback cb,
5550 : void *cb_cls)
5551 : {
5552 0 : struct PostgresClosure *pg = cls;
5553 0 : struct LookupPendingReservesContext lrc = {
5554 : .pg = pg,
5555 : .cb = cb,
5556 : .cb_cls = cb_cls
5557 : };
5558 0 : struct GNUNET_PQ_QueryParam params[] = {
5559 : GNUNET_PQ_query_param_end
5560 : };
5561 : enum GNUNET_DB_QueryStatus qs;
5562 :
5563 0 : check_connection (pg);
5564 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
5565 : "lookup_pending_reserves",
5566 : params,
5567 : &lookup_pending_reserves_cb,
5568 : &lrc);
5569 0 : if (lrc.qs < 0)
5570 0 : return lrc.qs;
5571 0 : return qs;
5572 : }
5573 :
5574 :
5575 : /**
5576 : * Closure for #lookup_reserve_tips_cb().
5577 : */
5578 : struct LookupTipsContext
5579 : {
5580 : /**
5581 : * Postgres context.
5582 : */
5583 : struct PostgresClosure *pg;
5584 :
5585 : /**
5586 : * Array with information about tips generated from this reserve.
5587 : */
5588 : struct TALER_MERCHANTDB_TipDetails *tips;
5589 :
5590 : /**
5591 : * Length of the @e tips array.
5592 : */
5593 : unsigned int tips_length;
5594 :
5595 : /**
5596 : * Set in case of errors.
5597 : */
5598 : enum GNUNET_DB_QueryStatus qs;
5599 : };
5600 :
5601 :
5602 : /**
5603 : * Function to be called with the results of a SELECT statement
5604 : * that has returned @a num_results results about accounts.
5605 : *
5606 : * @param[in,out] cls of type `struct LookupTipsContext *`
5607 : * @param result the postgres result
5608 : * @param num_results the number of results in @a result
5609 : */
5610 : static void
5611 0 : lookup_reserve_tips_cb (void *cls,
5612 : PGresult *result,
5613 : unsigned int num_results)
5614 : {
5615 0 : struct LookupTipsContext *ltc = cls;
5616 0 : struct PostgresClosure *pg = ltc->pg;
5617 :
5618 0 : GNUNET_array_grow (ltc->tips,
5619 : ltc->tips_length,
5620 : num_results);
5621 0 : for (unsigned int i = 0; i < num_results; i++)
5622 : {
5623 0 : struct TALER_MERCHANTDB_TipDetails *td = <c->tips[i];
5624 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5625 0 : GNUNET_PQ_result_spec_string ("justification",
5626 : &td->reason),
5627 0 : GNUNET_PQ_result_spec_auto_from_type ("tip_id",
5628 : &td->tip_id),
5629 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
5630 : &td->total_amount),
5631 : GNUNET_PQ_result_spec_end
5632 : };
5633 :
5634 0 : if (GNUNET_OK !=
5635 0 : GNUNET_PQ_extract_result (result,
5636 : rs,
5637 : i))
5638 : {
5639 0 : GNUNET_break (0);
5640 0 : ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
5641 0 : return;
5642 : }
5643 : }
5644 : }
5645 :
5646 :
5647 : /**
5648 : * Lookup reserve details.
5649 : *
5650 : * @param cls closure
5651 : * @param instance_id instance to lookup payments for
5652 : * @param reserve_pub public key of the reserve to inspect
5653 : * @param fetch_tips if true, also return information about tips
5654 : * @param cb function to call with reserve summary data
5655 : * @param cb_cls closure for @a cb
5656 : * @return transaction status
5657 : */
5658 : static enum GNUNET_DB_QueryStatus
5659 0 : postgres_lookup_reserve (void *cls,
5660 : const char *instance_id,
5661 : const struct TALER_ReservePublicKeyP *reserve_pub,
5662 : bool fetch_tips,
5663 : TALER_MERCHANTDB_ReserveDetailsCallback cb,
5664 : void *cb_cls)
5665 : {
5666 0 : struct PostgresClosure *pg = cls;
5667 0 : struct LookupTipsContext ltc = {
5668 : .pg = pg,
5669 : .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
5670 : };
5671 0 : struct GNUNET_PQ_QueryParam params[] = {
5672 0 : GNUNET_PQ_query_param_string (instance_id),
5673 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5674 : GNUNET_PQ_query_param_end
5675 : };
5676 : struct GNUNET_TIME_Timestamp creation_time;
5677 : struct GNUNET_TIME_Timestamp expiration_time;
5678 : struct TALER_Amount merchant_initial_balance;
5679 : struct TALER_Amount exchange_initial_balance;
5680 : struct TALER_Amount pickup_amount;
5681 : struct TALER_Amount committed_amount;
5682 : uint8_t active;
5683 0 : char *exchange_url = NULL;
5684 0 : char *payto_uri = NULL;
5685 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5686 0 : GNUNET_PQ_result_spec_timestamp ("creation_time",
5687 : &creation_time),
5688 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
5689 : &expiration_time),
5690 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
5691 : &merchant_initial_balance),
5692 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
5693 : &exchange_initial_balance),
5694 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
5695 : &pickup_amount),
5696 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
5697 : &committed_amount),
5698 0 : GNUNET_PQ_result_spec_auto_from_type ("active",
5699 : &active),
5700 0 : GNUNET_PQ_result_spec_allow_null (
5701 : GNUNET_PQ_result_spec_string ("exchange_url",
5702 : &exchange_url),
5703 : NULL),
5704 0 : GNUNET_PQ_result_spec_allow_null (
5705 : GNUNET_PQ_result_spec_string ("payto_uri",
5706 : &payto_uri),
5707 : NULL),
5708 : GNUNET_PQ_result_spec_end
5709 : };
5710 : enum GNUNET_DB_QueryStatus qs;
5711 :
5712 0 : check_connection (pg);
5713 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
5714 : "lookup_reserve",
5715 : params,
5716 : rs);
5717 0 : if (qs < 0)
5718 0 : return qs;
5719 0 : if (! fetch_tips)
5720 : {
5721 0 : cb (cb_cls,
5722 : creation_time,
5723 : expiration_time,
5724 : &merchant_initial_balance,
5725 : &exchange_initial_balance,
5726 : &pickup_amount,
5727 : &committed_amount,
5728 : (0 != active),
5729 : exchange_url,
5730 : payto_uri,
5731 : 0,
5732 : NULL);
5733 0 : GNUNET_PQ_cleanup_result (rs);
5734 0 : return qs;
5735 : }
5736 :
5737 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
5738 : "lookup_reserve_tips",
5739 : params,
5740 : &lookup_reserve_tips_cb,
5741 : <c);
5742 0 : if (qs < 0)
5743 0 : return qs;
5744 0 : if (ltc.qs >= 0)
5745 : {
5746 0 : cb (cb_cls,
5747 : creation_time,
5748 : expiration_time,
5749 : &merchant_initial_balance,
5750 : &exchange_initial_balance,
5751 : &pickup_amount,
5752 : &committed_amount,
5753 : 0 != active,
5754 : exchange_url,
5755 : payto_uri,
5756 : ltc.tips_length,
5757 0 : ltc.tips);
5758 : }
5759 0 : for (unsigned int i = 0; i<ltc.tips_length; i++)
5760 0 : GNUNET_free (ltc.tips[i].reason);
5761 0 : GNUNET_array_grow (ltc.tips,
5762 : ltc.tips_length,
5763 : 0);
5764 0 : GNUNET_PQ_cleanup_result (rs);
5765 0 : return ltc.qs;
5766 : }
5767 :
5768 :
5769 : /**
5770 : * Delete a reserve's private key.
5771 : *
5772 : * @param cls closure, typically a connection to the db
5773 : * @param instance_id which instance is the reserve tied to
5774 : * @param reserve_pub which reserve is to be deleted
5775 : * @return transaction status, usually
5776 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5777 : */
5778 : static enum GNUNET_DB_QueryStatus
5779 0 : postgres_delete_reserve (void *cls,
5780 : const char *instance_id,
5781 : const struct TALER_ReservePublicKeyP *reserve_pub)
5782 : {
5783 0 : struct PostgresClosure *pg = cls;
5784 0 : struct GNUNET_PQ_QueryParam params[] = {
5785 0 : GNUNET_PQ_query_param_string (instance_id),
5786 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5787 : GNUNET_PQ_query_param_end
5788 : };
5789 :
5790 0 : check_connection (pg);
5791 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5792 : "delete_reserve",
5793 : params);
5794 : }
5795 :
5796 :
5797 : /**
5798 : * Purge all of the information about a reserve, including tips.
5799 : *
5800 : * @param cls closure, typically a connection to the db
5801 : * @param instance_id which instance is the reserve tied to
5802 : * @param reserve_pub which reserve is to be purged
5803 : * @return transaction status, usually
5804 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5805 : */
5806 : static enum GNUNET_DB_QueryStatus
5807 0 : postgres_purge_reserve (void *cls,
5808 : const char *instance_id,
5809 : const struct TALER_ReservePublicKeyP *reserve_pub)
5810 : {
5811 0 : struct PostgresClosure *pg = cls;
5812 0 : struct GNUNET_PQ_QueryParam params[] = {
5813 0 : GNUNET_PQ_query_param_string (instance_id),
5814 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5815 : GNUNET_PQ_query_param_end
5816 : };
5817 :
5818 0 : check_connection (pg);
5819 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5820 : "purge_reserve",
5821 : params);
5822 : }
5823 :
5824 :
5825 : /**
5826 : * Closure for #lookup_reserve_for_tip_cb().
5827 : */
5828 : struct LookupReserveForTipContext
5829 : {
5830 : /**
5831 : * Postgres context.
5832 : */
5833 : struct PostgresClosure *pg;
5834 :
5835 : /**
5836 : * Public key of the reserve we found.
5837 : */
5838 : struct TALER_ReservePublicKeyP reserve_pub;
5839 :
5840 : /**
5841 : * How much money must be left in the reserve.
5842 : */
5843 : struct TALER_Amount required_amount;
5844 :
5845 : /**
5846 : * Set to the expiration time of the reserve we found.
5847 : * #GNUNET_TIME_UNIT_FOREVER_ABS if we found none.
5848 : */
5849 : struct GNUNET_TIME_Timestamp expiration;
5850 :
5851 : /**
5852 : * Error status.
5853 : */
5854 : enum TALER_ErrorCode ec;
5855 :
5856 : /**
5857 : * Did we find a good reserve?
5858 : */
5859 : bool ok;
5860 : };
5861 :
5862 :
5863 : /**
5864 : * How long must a reserve be at least still valid before we use
5865 : * it for a tip?
5866 : */
5867 : #define MIN_EXPIRATION GNUNET_TIME_UNIT_HOURS
5868 :
5869 :
5870 : /**
5871 : * Function to be called with the results of a SELECT statement
5872 : * that has returned @a num_results results about accounts.
5873 : *
5874 : * @param[in,out] cls of type `struct LookupReserveForTipContext *`
5875 : * @param result the postgres result
5876 : * @param num_results the number of results in @a result
5877 : */
5878 : static void
5879 0 : lookup_reserve_for_tip_cb (void *cls,
5880 : PGresult *result,
5881 : unsigned int num_results)
5882 : {
5883 0 : struct LookupReserveForTipContext *lac = cls;
5884 0 : struct PostgresClosure *pg = lac->pg;
5885 :
5886 0 : for (unsigned int i = 0; i < num_results; i++)
5887 : {
5888 : struct TALER_ReservePublicKeyP reserve_pub;
5889 : struct TALER_Amount committed_amount;
5890 : struct TALER_Amount remaining;
5891 : struct TALER_Amount initial_balance;
5892 : struct GNUNET_TIME_Timestamp expiration;
5893 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5894 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
5895 : &reserve_pub),
5896 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
5897 : &initial_balance),
5898 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
5899 : &committed_amount),
5900 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
5901 : &expiration),
5902 : GNUNET_PQ_result_spec_end
5903 : };
5904 :
5905 0 : if (GNUNET_OK !=
5906 0 : GNUNET_PQ_extract_result (result,
5907 : rs,
5908 : i))
5909 : {
5910 0 : GNUNET_break (0);
5911 0 : lac->ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
5912 0 : return;
5913 : }
5914 0 : if (0 >
5915 0 : TALER_amount_subtract (&remaining,
5916 : &initial_balance,
5917 : &committed_amount))
5918 : {
5919 0 : GNUNET_break (0);
5920 0 : continue;
5921 : }
5922 0 : if (0 >
5923 0 : TALER_amount_cmp (&remaining,
5924 0 : &lac->required_amount))
5925 : {
5926 : /* insufficient balance */
5927 0 : if (lac->ok)
5928 0 : continue; /* got another reserve */
5929 0 : lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
5930 0 : continue;
5931 : }
5932 0 : if ( (! GNUNET_TIME_absolute_is_never (lac->expiration.abs_time)) &&
5933 0 : GNUNET_TIME_timestamp_cmp (expiration, >, lac->expiration) &&
5934 0 : GNUNET_TIME_relative_cmp (
5935 : GNUNET_TIME_absolute_get_remaining (lac->expiration.abs_time),
5936 : >,
5937 : MIN_EXPIRATION) )
5938 : {
5939 : /* reserve expired */
5940 0 : if (lac->ok)
5941 0 : continue; /* got another reserve */
5942 0 : lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED;
5943 0 : continue;
5944 : }
5945 0 : lac->ok = true;
5946 0 : lac->ec = TALER_EC_NONE;
5947 0 : lac->expiration = expiration;
5948 0 : lac->reserve_pub = reserve_pub;
5949 : }
5950 : }
5951 :
5952 :
5953 : /**
5954 : * Authorize a tip over @a amount from reserve @a reserve_pub. Remember
5955 : * the authorization under @a tip_id for later, together with the
5956 : * @a justification.
5957 : *
5958 : * @param cls closure, typically a connection to the db
5959 : * @param instance_id which instance should generate the tip
5960 : * @param reserve_pub which reserve is debited, NULL to pick one in the DB
5961 : * @param amount how high is the tip (with fees)
5962 : * @param justification why was the tip approved
5963 : * @param next_url where to send the URL post tip pickup
5964 : * @param[out] tip_id set to the unique ID for the tip
5965 : * @param[out] expiration set to when the tip expires
5966 : * @return transaction status,
5967 : * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
5968 : * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND if the reserve is not known
5969 : * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
5970 : * #TALER_EC_GENERIC_DB_START_FAILED on hard DB errors
5971 : * #TALER_EC_GENERIC_DB_FETCH_FAILED on hard DB errors
5972 : * #TALER_EC_GENERIC_DB_STORE_FAILED on hard DB errors
5973 : * #TALER_EC_GENERIC_DB_INVARIANT_FAILURE on hard DB errors
5974 : * #TALER_EC_GENERIC_DB_SOFT_FAILURE soft DB errors (client should retry)
5975 : * #TALER_EC_NONE upon success
5976 : */
5977 : static enum TALER_ErrorCode
5978 0 : postgres_authorize_tip (void *cls,
5979 : const char *instance_id,
5980 : const struct TALER_ReservePublicKeyP *reserve_pub,
5981 : const struct TALER_Amount *amount,
5982 : const char *justification,
5983 : const char *next_url,
5984 : struct TALER_TipIdentifierP *tip_id,
5985 : struct GNUNET_TIME_Timestamp *expiration)
5986 : {
5987 0 : struct PostgresClosure *pg = cls;
5988 0 : unsigned int retries = 0;
5989 : enum GNUNET_DB_QueryStatus qs;
5990 : struct TALER_Amount tips_committed;
5991 : struct TALER_Amount exchange_initial_balance;
5992 : const struct TALER_ReservePublicKeyP *reserve_pubp;
5993 0 : struct LookupReserveForTipContext lac = {
5994 : .pg = pg,
5995 : .required_amount = *amount,
5996 : .expiration = GNUNET_TIME_UNIT_FOREVER_TS
5997 : };
5998 :
5999 0 : check_connection (pg);
6000 0 : RETRY:
6001 0 : reserve_pubp = reserve_pub;
6002 0 : if (MAX_RETRIES < ++retries)
6003 : {
6004 0 : GNUNET_break (0);
6005 : return
6006 0 : TALER_EC_GENERIC_DB_SOFT_FAILURE;
6007 : }
6008 0 : if (GNUNET_OK !=
6009 0 : postgres_start (pg,
6010 : "authorize tip"))
6011 : {
6012 0 : GNUNET_break (0);
6013 0 : return TALER_EC_GENERIC_DB_START_FAILED;
6014 : }
6015 0 : if (NULL == reserve_pubp)
6016 : {
6017 0 : struct GNUNET_PQ_QueryParam params[] = {
6018 0 : GNUNET_PQ_query_param_string (instance_id),
6019 : GNUNET_PQ_query_param_end
6020 : };
6021 :
6022 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6023 : "lookup_reserve_for_tip",
6024 : params,
6025 : &lookup_reserve_for_tip_cb,
6026 : &lac);
6027 0 : switch (qs)
6028 : {
6029 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
6030 0 : postgres_rollback (pg);
6031 0 : goto RETRY;
6032 0 : case GNUNET_DB_STATUS_HARD_ERROR:
6033 0 : GNUNET_break (0);
6034 0 : postgres_rollback (pg);
6035 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
6036 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
6037 0 : postgres_rollback (pg);
6038 0 : return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND;
6039 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
6040 : default:
6041 0 : break;
6042 : }
6043 0 : if (TALER_EC_NONE != lac.ec)
6044 : {
6045 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6046 : "Enabling tip reserved failed with status %d\n",
6047 : lac.ec);
6048 0 : postgres_rollback (pg);
6049 0 : return lac.ec;
6050 : }
6051 0 : GNUNET_assert (lac.ok);
6052 0 : reserve_pubp = &lac.reserve_pub;
6053 : }
6054 :
6055 : {
6056 0 : struct GNUNET_PQ_QueryParam params[] = {
6057 0 : GNUNET_PQ_query_param_string (instance_id),
6058 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
6059 : GNUNET_PQ_query_param_end
6060 : };
6061 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6062 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
6063 : expiration),
6064 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
6065 : &tips_committed),
6066 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
6067 : &exchange_initial_balance),
6068 : GNUNET_PQ_result_spec_end
6069 : };
6070 :
6071 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6072 : "lookup_reserve_status",
6073 : params,
6074 : rs);
6075 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6076 : {
6077 0 : postgres_rollback (pg);
6078 0 : goto RETRY;
6079 : }
6080 0 : if (qs < 0)
6081 : {
6082 0 : GNUNET_break (0);
6083 0 : postgres_rollback (pg);
6084 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
6085 : }
6086 0 : if (0 == qs)
6087 : {
6088 0 : postgres_rollback (pg);
6089 0 : return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND;
6090 : }
6091 : }
6092 : {
6093 : struct TALER_Amount remaining;
6094 :
6095 0 : if (0 >
6096 0 : TALER_amount_subtract (&remaining,
6097 : &exchange_initial_balance,
6098 : &tips_committed))
6099 : {
6100 0 : GNUNET_break (0);
6101 0 : postgres_rollback (pg);
6102 0 : return TALER_EC_GENERIC_DB_INVARIANT_FAILURE;
6103 : }
6104 0 : if (0 >
6105 0 : TALER_amount_cmp (&remaining,
6106 : amount))
6107 : {
6108 0 : postgres_rollback (pg);
6109 0 : return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
6110 : }
6111 : }
6112 0 : GNUNET_assert (0 <=
6113 : TALER_amount_add (&tips_committed,
6114 : &tips_committed,
6115 : amount));
6116 : {
6117 0 : struct GNUNET_PQ_QueryParam params[] = {
6118 0 : GNUNET_PQ_query_param_string (instance_id),
6119 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
6120 0 : TALER_PQ_query_param_amount (&tips_committed),
6121 : GNUNET_PQ_query_param_end
6122 : };
6123 :
6124 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6125 : "update_reserve_tips_committed",
6126 : params);
6127 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6128 : {
6129 0 : postgres_rollback (pg);
6130 0 : goto RETRY;
6131 : }
6132 0 : if (qs < 0)
6133 : {
6134 0 : GNUNET_break (0);
6135 0 : postgres_rollback (pg);
6136 0 : return TALER_EC_GENERIC_DB_STORE_FAILED;
6137 : }
6138 : }
6139 0 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
6140 : tip_id,
6141 : sizeof (*tip_id));
6142 : {
6143 0 : struct GNUNET_PQ_QueryParam params[] = {
6144 0 : GNUNET_PQ_query_param_string (instance_id),
6145 0 : GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
6146 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6147 0 : GNUNET_PQ_query_param_string (justification),
6148 0 : GNUNET_PQ_query_param_string (next_url),
6149 0 : GNUNET_PQ_query_param_timestamp (expiration),
6150 0 : TALER_PQ_query_param_amount (amount),
6151 : GNUNET_PQ_query_param_end
6152 : };
6153 :
6154 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6155 : "insert_tip",
6156 : params);
6157 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6158 : {
6159 0 : postgres_rollback (pg);
6160 0 : goto RETRY;
6161 : }
6162 0 : if (qs < 0)
6163 : {
6164 0 : GNUNET_break (0);
6165 0 : postgres_rollback (pg);
6166 0 : return TALER_EC_GENERIC_DB_STORE_FAILED;
6167 : }
6168 : }
6169 0 : qs = postgres_commit (pg);
6170 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6171 0 : goto RETRY;
6172 0 : if (qs < 0)
6173 : {
6174 0 : GNUNET_break (0);
6175 0 : postgres_rollback (pg);
6176 0 : return TALER_EC_GENERIC_DB_COMMIT_FAILED;
6177 : }
6178 0 : return TALER_EC_NONE;
6179 : }
6180 :
6181 :
6182 : /**
6183 : * Closure for #lookup_signatures_cb().
6184 : */
6185 : struct LookupSignaturesContext
6186 : {
6187 : /**
6188 : * Length of the @e sigs array
6189 : */
6190 : unsigned int sigs_length;
6191 :
6192 : /**
6193 : * Where to store the signatures.
6194 : */
6195 : struct TALER_BlindedDenominationSignature *sigs;
6196 : };
6197 :
6198 :
6199 : /**
6200 : * Function to be called with the results of a SELECT statement
6201 : * that has returned @a num_results results about accounts.
6202 : *
6203 : * @param[in,out] cls of type `struct LookupSignaturesContext *`
6204 : * @param result the postgres result
6205 : * @param num_results the number of results in @a result
6206 : */
6207 : static void
6208 0 : lookup_signatures_cb (void *cls,
6209 : PGresult *result,
6210 : unsigned int num_results)
6211 : {
6212 0 : struct LookupSignaturesContext *lsc = cls;
6213 :
6214 0 : for (unsigned int i = 0; i < num_results; i++)
6215 : {
6216 : uint32_t offset;
6217 : struct TALER_BlindedDenominationSignature bsig;
6218 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6219 0 : GNUNET_PQ_result_spec_uint32 ("coin_offset",
6220 : &offset),
6221 0 : TALER_PQ_result_spec_blinded_denom_sig ("blind_sig",
6222 : &bsig),
6223 : GNUNET_PQ_result_spec_end
6224 : };
6225 :
6226 0 : if (GNUNET_OK !=
6227 0 : GNUNET_PQ_extract_result (result,
6228 : rs,
6229 : i))
6230 : {
6231 0 : GNUNET_break (0);
6232 0 : return;
6233 : }
6234 0 : if (offset >= lsc->sigs_length)
6235 : {
6236 0 : GNUNET_break_op (0);
6237 0 : GNUNET_PQ_cleanup_result (rs);
6238 0 : continue;
6239 : }
6240 : /* Must be NULL due to UNIQUE constraint on offset and
6241 : requirement that client launched us with 'sigs'
6242 : pre-initialized to NULL. */
6243 0 : lsc->sigs[offset] = bsig;
6244 : }
6245 : }
6246 :
6247 :
6248 : /**
6249 : * Lookup pickup details for pickup @a pickup_id.
6250 : *
6251 : * @param cls closure, typically a connection to the db
6252 : * @param instance_id which instance should we lookup tip details for
6253 : * @param tip_id which tip should we lookup details on
6254 : * @param pickup_id which pickup should we lookup details on
6255 : * @param[out] exchange_url which exchange is the tip withdrawn from
6256 : * @param[out] reserve_priv private key the tip is withdrawn from (set if still available!)
6257 : * @param sigs_length length of the @a sigs array
6258 : * @param[out] sigs set to the (blind) signatures we have for this @a pickup_id,
6259 : * those that are unavailable are left at NULL
6260 : * @return transaction status
6261 : */
6262 : static enum GNUNET_DB_QueryStatus
6263 0 : postgres_lookup_pickup (void *cls,
6264 : const char *instance_id,
6265 : const struct TALER_TipIdentifierP *tip_id,
6266 : const struct TALER_PickupIdentifierP *pickup_id,
6267 : char **exchange_url,
6268 : struct TALER_ReservePrivateKeyP *reserve_priv,
6269 : unsigned int sigs_length,
6270 : struct TALER_BlindedDenominationSignature sigs[])
6271 : {
6272 0 : struct PostgresClosure *pg = cls;
6273 : uint64_t pickup_serial;
6274 :
6275 : {
6276 0 : struct GNUNET_PQ_QueryParam params[] = {
6277 0 : GNUNET_PQ_query_param_string (instance_id),
6278 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6279 0 : GNUNET_PQ_query_param_auto_from_type (pickup_id),
6280 : GNUNET_PQ_query_param_end
6281 : };
6282 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6283 0 : GNUNET_PQ_result_spec_string ("exchange_url",
6284 : exchange_url),
6285 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
6286 : reserve_priv),
6287 0 : GNUNET_PQ_result_spec_uint64 ("pickup_serial",
6288 : &pickup_serial),
6289 : GNUNET_PQ_result_spec_end
6290 : };
6291 : enum GNUNET_DB_QueryStatus qs;
6292 :
6293 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6294 : "lookup_pickup",
6295 : params,
6296 : rs);
6297 0 : if (qs <= 0)
6298 0 : return qs;
6299 : }
6300 : {
6301 0 : struct GNUNET_PQ_QueryParam params[] = {
6302 0 : GNUNET_PQ_query_param_uint64 (&pickup_serial),
6303 : GNUNET_PQ_query_param_end
6304 : };
6305 0 : struct LookupSignaturesContext lsc = {
6306 : .sigs_length = sigs_length,
6307 : .sigs = sigs
6308 : };
6309 :
6310 0 : return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6311 : "lookup_pickup_signatures",
6312 : params,
6313 : &lookup_signatures_cb,
6314 : &lsc);
6315 : }
6316 : }
6317 :
6318 :
6319 : /**
6320 : * Lookup tip details for tip @a tip_id.
6321 : *
6322 : * @param cls closure, typically a connection to the db
6323 : * @param instance_id which instance should we lookup tip details for
6324 : * @param tip_id which tip should we lookup details on
6325 : * @param[out] total_authorized amount how high is the tip (with fees)
6326 : * @param[out] total_picked_up how much of the tip was so far picked up (with fees)
6327 : * @param[out] expiration set to when the tip expires
6328 : * @param[out] exchange_url set to the exchange URL where the reserve is
6329 : * @param[out] reserve_priv set to private key of reserve to be debited
6330 : * @return transaction status
6331 : */
6332 : static enum GNUNET_DB_QueryStatus
6333 0 : postgres_lookup_tip (void *cls,
6334 : const char *instance_id,
6335 : const struct TALER_TipIdentifierP *tip_id,
6336 : struct TALER_Amount *total_authorized,
6337 : struct TALER_Amount *total_picked_up,
6338 : struct GNUNET_TIME_Timestamp *expiration,
6339 : char **exchange_url,
6340 : struct TALER_ReservePrivateKeyP *reserve_priv)
6341 : {
6342 0 : struct PostgresClosure *pg = cls;
6343 0 : struct GNUNET_PQ_QueryParam params[] = {
6344 0 : GNUNET_PQ_query_param_string (instance_id),
6345 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6346 : GNUNET_PQ_query_param_end
6347 : };
6348 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6349 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6350 : total_authorized),
6351 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
6352 : total_picked_up),
6353 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
6354 : expiration),
6355 0 : GNUNET_PQ_result_spec_string ("exchange_url",
6356 : exchange_url),
6357 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
6358 : reserve_priv),
6359 : GNUNET_PQ_result_spec_end
6360 : };
6361 :
6362 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6363 : "lookup_tip",
6364 : params,
6365 : rs);
6366 : }
6367 :
6368 :
6369 : /**
6370 : * Context used for postgres_lookup_tips().
6371 : */
6372 : struct LookupMerchantTipsContext
6373 : {
6374 : /**
6375 : * Postgres context.
6376 : */
6377 : struct PostgresClosure *pg;
6378 :
6379 : /**
6380 : * Function to call with the results.
6381 : */
6382 : TALER_MERCHANTDB_TipsCallback cb;
6383 :
6384 : /**
6385 : * Closure for @a cb.
6386 : */
6387 : void *cb_cls;
6388 :
6389 : /**
6390 : * Internal result.
6391 : */
6392 : enum GNUNET_DB_QueryStatus qs;
6393 : };
6394 :
6395 :
6396 : /**
6397 : * Function to be called with the results of a SELECT statement
6398 : * that has returned @a num_results results about tips.
6399 : *
6400 : * @param[in,out] cls of type `struct LookupTipsContext *`
6401 : * @param result the postgres result
6402 : * @param num_results the number of results in @a result
6403 : */
6404 : static void
6405 0 : lookup_tips_cb (void *cls,
6406 : PGresult *result,
6407 : unsigned int num_results)
6408 : {
6409 0 : struct LookupMerchantTipsContext *plc = cls;
6410 0 : struct PostgresClosure *pg = plc->pg;
6411 :
6412 0 : for (unsigned int i = 0; i < num_results; i++)
6413 : {
6414 : uint64_t row_id;
6415 : struct TALER_TipIdentifierP tip_id;
6416 : struct TALER_Amount tip_amount;
6417 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6418 0 : GNUNET_PQ_result_spec_uint64 ("tip_serial",
6419 : &row_id),
6420 0 : GNUNET_PQ_result_spec_auto_from_type ("tip_id",
6421 : &tip_id),
6422 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6423 : &tip_amount),
6424 : GNUNET_PQ_result_spec_end
6425 : };
6426 :
6427 0 : if (GNUNET_OK !=
6428 0 : GNUNET_PQ_extract_result (result,
6429 : rs,
6430 : i))
6431 : {
6432 0 : GNUNET_break (0);
6433 0 : plc->qs = GNUNET_DB_STATUS_HARD_ERROR;
6434 0 : return;
6435 : }
6436 0 : plc->cb (plc->cb_cls,
6437 : row_id,
6438 : tip_id,
6439 : tip_amount);
6440 0 : GNUNET_PQ_cleanup_result (rs);
6441 : }
6442 : }
6443 :
6444 :
6445 : /**
6446 : * Lookup tips
6447 : *
6448 : * @param cls closure, typically a connection to the db
6449 : * @param instance_id which instance should we lookup tips for
6450 : * @param expired should we include expired tips?
6451 : * @param limit maximum number of results to return, positive for
6452 : * ascending row id, negative for descending
6453 : * @param offset row id to start returning results from
6454 : * @param cb function to call with tip data
6455 : * @param cb_cls closure for @a cb
6456 : * @return transaction status
6457 : */
6458 : static enum GNUNET_DB_QueryStatus
6459 0 : postgres_lookup_tips (void *cls,
6460 : const char *instance_id,
6461 : enum TALER_EXCHANGE_YesNoAll expired,
6462 : int64_t limit,
6463 : uint64_t offset,
6464 : TALER_MERCHANTDB_TipsCallback cb,
6465 : void *cb_cls)
6466 : {
6467 0 : struct PostgresClosure *pg = cls;
6468 0 : struct LookupMerchantTipsContext plc = {
6469 : .pg = pg,
6470 : .cb = cb,
6471 : .cb_cls = cb_cls
6472 : };
6473 0 : uint64_t ulimit = (limit > 0) ? limit : -limit;
6474 : uint8_t bexpired;
6475 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
6476 0 : struct GNUNET_PQ_QueryParam params[] = {
6477 0 : GNUNET_PQ_query_param_string (instance_id),
6478 0 : GNUNET_PQ_query_param_uint64 (&ulimit),
6479 0 : GNUNET_PQ_query_param_uint64 (&offset),
6480 0 : GNUNET_PQ_query_param_absolute_time (&now),
6481 0 : GNUNET_PQ_query_param_auto_from_type (&bexpired),
6482 : GNUNET_PQ_query_param_end
6483 : };
6484 : enum GNUNET_DB_QueryStatus qs;
6485 : char stmt[128];
6486 :
6487 0 : bexpired = (TALER_EXCHANGE_YNA_YES == expired);
6488 0 : GNUNET_snprintf (stmt,
6489 : sizeof (stmt),
6490 : "lookup_tips_%s%s",
6491 : (limit > 0) ? "inc" : "dec",
6492 : (TALER_EXCHANGE_YNA_ALL == expired) ? "" : "_expired");
6493 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6494 : stmt,
6495 : params,
6496 : &lookup_tips_cb,
6497 : &plc);
6498 0 : if (0 != plc.qs)
6499 0 : return plc.qs;
6500 0 : return qs;
6501 : }
6502 :
6503 :
6504 : /**
6505 : * Closure for #lookup_pickup_details_cb().
6506 : */
6507 : struct LookupTipDetailsContext
6508 : {
6509 : /**
6510 : * Length of the @e sigs array
6511 : */
6512 : unsigned int *pickups_length;
6513 :
6514 : /**
6515 : * Where to store the signatures.
6516 : */
6517 : struct TALER_MERCHANTDB_PickupDetails **pickups;
6518 :
6519 : /**
6520 : * Database handle.
6521 : */
6522 : struct PostgresClosure *pg;
6523 :
6524 : /**
6525 : * Transaction status.
6526 : */
6527 : enum GNUNET_DB_QueryStatus qs;
6528 :
6529 : };
6530 :
6531 :
6532 : /**
6533 : * Function to be called with the results of a SELECT statement
6534 : * that has returned @a num_results results about pickups.
6535 : *
6536 : * @param[in,out] cls of type `struct LookupTipDetailsContext *`
6537 : * @param result the postgres result
6538 : * @param num_results the number of results in @a result
6539 : */
6540 : static void
6541 0 : lookup_pickup_details_cb (void *cls,
6542 : PGresult *result,
6543 : unsigned int num_results)
6544 : {
6545 0 : struct LookupTipDetailsContext *ltdc = cls;
6546 0 : struct PostgresClosure *pg = ltdc->pg;
6547 :
6548 0 : *ltdc->pickups_length = num_results;
6549 0 : *ltdc->pickups = GNUNET_new_array (num_results,
6550 : struct TALER_MERCHANTDB_PickupDetails);
6551 0 : for (unsigned int i = 0; i < num_results; i++)
6552 : {
6553 0 : struct TALER_MERCHANTDB_PickupDetails *pd = &((*ltdc->pickups)[i]);
6554 0 : uint64_t num_planchets = 0;
6555 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6556 0 : GNUNET_PQ_result_spec_auto_from_type ("pickup_id",
6557 : &pd->pickup_id),
6558 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6559 : &pd->requested_amount),
6560 0 : GNUNET_PQ_result_spec_uint64 ("num_planchets",
6561 : &num_planchets),
6562 : GNUNET_PQ_result_spec_end
6563 : };
6564 :
6565 0 : if (GNUNET_OK !=
6566 0 : GNUNET_PQ_extract_result (result,
6567 : rs,
6568 : i))
6569 : {
6570 0 : GNUNET_break (0);
6571 0 : ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
6572 0 : GNUNET_array_grow (*ltdc->pickups,
6573 : *ltdc->pickups_length,
6574 : 0);
6575 0 : return;
6576 : }
6577 :
6578 0 : pd->num_planchets = num_planchets;
6579 : }
6580 : }
6581 :
6582 :
6583 : /**
6584 : * Lookup tip details for tip @a tip_id.
6585 : *
6586 : * @param cls closure, typically a connection to the db
6587 : * @param instance_id which instance should we lookup tip details for
6588 : * @param tip_id which tip should we lookup details on
6589 : * @param fpu should we fetch details about individual pickups
6590 : * @param[out] total_authorized amount how high is the tip (with fees)
6591 : * @param[out] total_picked_up how much of the tip was so far picked up (with fees)
6592 : * @param[out] justification why was the tip approved
6593 : * @param[out] expiration set to when the tip expires
6594 : * @param[out] reserve_pub set to which reserve is debited
6595 : * @param[out] pickups_length set to the length of @e pickups
6596 : * @param[out] pickups if @a fpu is true, set to details about the pickup operations
6597 : * @return transaction status,
6598 : */
6599 : static enum GNUNET_DB_QueryStatus
6600 0 : postgres_lookup_tip_details (void *cls,
6601 : const char *instance_id,
6602 : const struct TALER_TipIdentifierP *tip_id,
6603 : bool fpu,
6604 : struct TALER_Amount *total_authorized,
6605 : struct TALER_Amount *total_picked_up,
6606 : char **justification,
6607 : struct GNUNET_TIME_Timestamp *expiration,
6608 : struct TALER_ReservePublicKeyP *reserve_pub,
6609 : unsigned int *pickups_length,
6610 : struct TALER_MERCHANTDB_PickupDetails **pickups)
6611 : {
6612 0 : struct PostgresClosure *pg = cls;
6613 : uint64_t tip_serial;
6614 : enum GNUNET_DB_QueryStatus qs;
6615 : {
6616 0 : struct GNUNET_PQ_QueryParam params[] = {
6617 0 : GNUNET_PQ_query_param_string (instance_id),
6618 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6619 : GNUNET_PQ_query_param_end
6620 : };
6621 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6622 0 : GNUNET_PQ_result_spec_uint64 ("tip_serial",
6623 : &tip_serial),
6624 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6625 : total_authorized),
6626 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
6627 : total_picked_up),
6628 0 : GNUNET_PQ_result_spec_string ("justification",
6629 : justification),
6630 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
6631 : expiration),
6632 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
6633 : reserve_pub),
6634 : GNUNET_PQ_result_spec_end
6635 : };
6636 :
6637 0 : check_connection (pg);
6638 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6639 : "lookup_tip_details",
6640 : params,
6641 : rs);
6642 0 : if (qs <= 0)
6643 0 : return qs;
6644 0 : if (! fpu)
6645 : {
6646 0 : *pickups_length = 0;
6647 0 : *pickups = NULL;
6648 0 : return qs;
6649 : }
6650 : }
6651 : {
6652 0 : struct GNUNET_PQ_QueryParam params[] = {
6653 0 : GNUNET_PQ_query_param_uint64 (&tip_serial),
6654 : GNUNET_PQ_query_param_end
6655 : };
6656 :
6657 0 : struct LookupTipDetailsContext ltdc = {
6658 : .pickups_length = pickups_length,
6659 : .pickups = pickups,
6660 : .pg = pg,
6661 : .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
6662 : };
6663 :
6664 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6665 : "lookup_pickup_details",
6666 : params,
6667 : &lookup_pickup_details_cb,
6668 : <dc);
6669 0 : if (qs < 0)
6670 0 : return qs;
6671 0 : return ltdc.qs;
6672 : }
6673 : }
6674 :
6675 :
6676 : /**
6677 : * Insert details about a tip pickup operation. The @a total_picked_up
6678 : * UPDATES the total amount under the @a tip_id, while the @a
6679 : * total_requested is the amount to be associated with this @a pickup_id.
6680 : * While there is usually only one pickup event that picks up the entire
6681 : * amount, our schema allows for wallets to pick up the amount incrementally
6682 : * over multiple pick up operations.
6683 : *
6684 : * @param cls closure, typically a connection to the db
6685 : * @param instance_id which instance gave the tip
6686 : * @param tip_id the unique ID for the tip
6687 : * @param total_picked_up how much was picked up overall at this
6688 : * point (includes @a total_requested)
6689 : * @param pickup_id unique ID for the operation
6690 : * @param total_requested how much is being picked up in this operation
6691 : * @return transaction status, usually
6692 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
6693 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
6694 : */
6695 : static enum GNUNET_DB_QueryStatus
6696 0 : postgres_insert_pickup (void *cls,
6697 : const char *instance_id,
6698 : const struct TALER_TipIdentifierP *tip_id,
6699 : const struct TALER_Amount *total_picked_up,
6700 : const struct TALER_PickupIdentifierP *pickup_id,
6701 : const struct TALER_Amount *total_requested)
6702 : {
6703 0 : struct PostgresClosure *pg = cls;
6704 : enum GNUNET_DB_QueryStatus qs;
6705 :
6706 : {
6707 0 : struct GNUNET_PQ_QueryParam params[] = {
6708 0 : GNUNET_PQ_query_param_string (instance_id),
6709 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6710 0 : GNUNET_PQ_query_param_auto_from_type (pickup_id),
6711 0 : TALER_PQ_query_param_amount (total_requested),
6712 : GNUNET_PQ_query_param_end
6713 : };
6714 :
6715 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6716 : "insert_pickup",
6717 : params);
6718 0 : if (0 > qs)
6719 0 : return qs;
6720 : }
6721 :
6722 : {
6723 0 : struct GNUNET_PQ_QueryParam params[] = {
6724 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6725 0 : TALER_PQ_query_param_amount (total_picked_up),
6726 : GNUNET_PQ_query_param_end
6727 : };
6728 :
6729 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6730 : "update_picked_up_tip",
6731 : params);
6732 0 : if (0 > qs)
6733 0 : return qs;
6734 : }
6735 : {
6736 : uint64_t reserve_serial;
6737 : struct TALER_Amount reserve_picked_up;
6738 : {
6739 0 : struct GNUNET_PQ_QueryParam params[] = {
6740 0 : GNUNET_PQ_query_param_string (instance_id),
6741 0 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6742 : GNUNET_PQ_query_param_end
6743 : };
6744 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6745 0 : GNUNET_PQ_result_spec_uint64 ("reserve_serial",
6746 : &reserve_serial),
6747 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
6748 : &reserve_picked_up),
6749 : GNUNET_PQ_result_spec_end
6750 :
6751 : };
6752 :
6753 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6754 : "lookup_picked_up_reserve",
6755 : params,
6756 : rs);
6757 0 : if (0 > qs)
6758 0 : return qs;
6759 : }
6760 0 : if (0 >=
6761 0 : TALER_amount_add (&reserve_picked_up,
6762 : &reserve_picked_up,
6763 : total_requested))
6764 : {
6765 0 : GNUNET_break (0);
6766 0 : return GNUNET_DB_STATUS_HARD_ERROR;
6767 : }
6768 :
6769 : {
6770 0 : struct GNUNET_PQ_QueryParam params[] = {
6771 0 : GNUNET_PQ_query_param_uint64 (&reserve_serial),
6772 0 : TALER_PQ_query_param_amount (&reserve_picked_up),
6773 : GNUNET_PQ_query_param_end
6774 : };
6775 :
6776 0 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6777 : "update_picked_up_reserve",
6778 : params);
6779 0 : if (0 > qs)
6780 0 : return qs;
6781 : }
6782 : }
6783 0 : return qs;
6784 : }
6785 :
6786 :
6787 : /**
6788 : * Insert blind signature obtained from the exchange during a
6789 : * tip pickup operation.
6790 : *
6791 : * @param cls closure, typically a connection to the db
6792 : * @param pickup_id unique ID for the operation
6793 : * @param offset offset of the blind signature for the pickup
6794 : * @param blind_sig the blind signature
6795 : * @return transaction status, usually
6796 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
6797 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
6798 : */
6799 : static enum GNUNET_DB_QueryStatus
6800 0 : postgres_insert_pickup_blind_signature (
6801 : void *cls,
6802 : const struct TALER_PickupIdentifierP *pickup_id,
6803 : uint32_t offset,
6804 : const struct TALER_BlindedDenominationSignature *blind_sig)
6805 : {
6806 0 : struct PostgresClosure *pg = cls;
6807 0 : struct GNUNET_PQ_QueryParam params[] = {
6808 0 : GNUNET_PQ_query_param_auto_from_type (pickup_id),
6809 0 : GNUNET_PQ_query_param_uint32 (&offset),
6810 0 : TALER_PQ_query_param_blinded_denom_sig (blind_sig),
6811 : GNUNET_PQ_query_param_end
6812 : };
6813 :
6814 0 : check_connection (pg);
6815 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
6816 : "insert_pickup_blind_signature",
6817 : params);
6818 : }
6819 :
6820 :
6821 : /**
6822 : * Establish connection to the database.
6823 : *
6824 : * @param cls plugin context
6825 : * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
6826 : */
6827 : static int
6828 0 : postgres_connect (void *cls)
6829 : {
6830 0 : struct PostgresClosure *pg = cls;
6831 0 : struct GNUNET_PQ_PreparedStatement ps[] = {
6832 0 : GNUNET_PQ_make_prepare ("end_transaction",
6833 : "COMMIT",
6834 : 0),
6835 : /* for call_with_accounts(), part of postgres_lookup_instances() */
6836 0 : GNUNET_PQ_make_prepare ("lookup_instance_private_key",
6837 : "SELECT"
6838 : " merchant_priv"
6839 : " FROM merchant_keys"
6840 : " WHERE merchant_serial=$1",
6841 : 1),
6842 : /* for find_instances_cb(), part of postgres_lookup_instances() */
6843 0 : GNUNET_PQ_make_prepare ("lookup_accounts",
6844 : "SELECT"
6845 : " h_wire"
6846 : ",salt"
6847 : ",payto_uri"
6848 : ",active"
6849 : " FROM merchant_accounts"
6850 : " WHERE merchant_serial=$1",
6851 : 1),
6852 : /* for postgres_lookup_instances() */
6853 0 : GNUNET_PQ_make_prepare ("lookup_instance_auth",
6854 : "SELECT"
6855 : " auth_hash"
6856 : ",auth_salt"
6857 : " FROM merchant_instances"
6858 : " WHERE merchant_id=$1",
6859 : 1),
6860 : /* for postgres_lookup_instances() */
6861 0 : GNUNET_PQ_make_prepare ("lookup_instances",
6862 : "SELECT"
6863 : " merchant_serial"
6864 : ",merchant_pub"
6865 : ",auth_hash"
6866 : ",auth_salt"
6867 : ",merchant_id"
6868 : ",merchant_name"
6869 : ",address"
6870 : ",jurisdiction"
6871 : ",default_max_deposit_fee_val"
6872 : ",default_max_deposit_fee_frac"
6873 : ",default_max_wire_fee_val"
6874 : ",default_max_wire_fee_frac"
6875 : ",default_wire_fee_amortization"
6876 : ",default_wire_transfer_delay"
6877 : ",default_pay_delay"
6878 : ",website"
6879 : ",email"
6880 : ",logo"
6881 : " FROM merchant_instances",
6882 : 0),
6883 : /* for postgres_lookup_instance() */
6884 0 : GNUNET_PQ_make_prepare ("lookup_instance",
6885 : "SELECT"
6886 : " merchant_serial"
6887 : ",merchant_pub"
6888 : ",auth_hash"
6889 : ",auth_salt"
6890 : ",merchant_id"
6891 : ",merchant_name"
6892 : ",address"
6893 : ",jurisdiction"
6894 : ",default_max_deposit_fee_val"
6895 : ",default_max_deposit_fee_frac"
6896 : ",default_max_wire_fee_val"
6897 : ",default_max_wire_fee_frac"
6898 : ",default_wire_fee_amortization"
6899 : ",default_wire_transfer_delay"
6900 : ",default_pay_delay"
6901 : ",website"
6902 : ",email"
6903 : ",logo"
6904 : " FROM merchant_instances"
6905 : " WHERE merchant_id=$1",
6906 : 1),
6907 : /* for postgres_insert_instance() */
6908 0 : GNUNET_PQ_make_prepare ("insert_instance",
6909 : "INSERT INTO merchant_instances"
6910 : "(merchant_pub"
6911 : ",auth_hash"
6912 : ",auth_salt"
6913 : ",merchant_id"
6914 : ",merchant_name"
6915 : ",address"
6916 : ",jurisdiction"
6917 : ",default_max_deposit_fee_val"
6918 : ",default_max_deposit_fee_frac"
6919 : ",default_max_wire_fee_val"
6920 : ",default_max_wire_fee_frac"
6921 : ",default_wire_fee_amortization"
6922 : ",default_wire_transfer_delay"
6923 : ",default_pay_delay"
6924 : ",website"
6925 : ",email"
6926 : ",logo)"
6927 : "VALUES"
6928 : "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)",
6929 : 14),
6930 : /* for postgres_insert_instance() */
6931 0 : GNUNET_PQ_make_prepare ("insert_keys",
6932 : "INSERT INTO merchant_keys"
6933 : "(merchant_priv"
6934 : ",merchant_serial)"
6935 : " SELECT $1, merchant_serial"
6936 : " FROM merchant_instances"
6937 : " WHERE merchant_id=$2",
6938 : 2),
6939 : /* for postgres_account_kyc_set_status */
6940 0 : GNUNET_PQ_make_prepare ("upsert_account_kyc",
6941 : "INSERT INTO merchant_kyc"
6942 : "(kyc_timestamp"
6943 : ",kyc_ok"
6944 : ",exchange_kyc_serial"
6945 : ",account_serial"
6946 : ",exchange_url"
6947 : ",exchange_pub"
6948 : ",exchange_sig)"
6949 : " SELECT $5, $6, $4, account_serial, $3, $7, $8"
6950 : " FROM merchant_instances"
6951 : " JOIN merchant_accounts USING (merchant_serial)"
6952 : " WHERE merchant_id=$1"
6953 : " AND h_wire=$2"
6954 : " ON CONFLICT(account_serial,exchange_url) DO "
6955 : "UPDATE"
6956 : " SET exchange_kyc_serial=$4"
6957 : " ,kyc_timestamp=$5"
6958 : " ,kyc_ok=$6"
6959 : " ,exchange_pub=$7"
6960 : " ,exchange_sig=$8",
6961 : 8),
6962 : /* for postgres_account_kyc_get_status */
6963 0 : GNUNET_PQ_make_prepare ("lookup_kyc_status",
6964 : "SELECT"
6965 : " h_wire"
6966 : ",exchange_kyc_serial"
6967 : ",payto_uri"
6968 : ",exchange_url"
6969 : ",kyc_timestamp"
6970 : ",kyc_ok"
6971 : " FROM merchant_instances"
6972 : " JOIN merchant_accounts"
6973 : " USING (merchant_serial)"
6974 : " JOIN merchant_kyc"
6975 : " USING (account_serial)"
6976 : " WHERE merchant_instances.merchant_id=$1",
6977 : 1),
6978 : /* for postgres_insert_account() */
6979 0 : GNUNET_PQ_make_prepare ("insert_account",
6980 : "INSERT INTO merchant_accounts"
6981 : "(merchant_serial"
6982 : ",h_wire"
6983 : ",salt"
6984 : ",payto_uri"
6985 : ",active)"
6986 : " SELECT merchant_serial, $2, $3, $4, $5"
6987 : " FROM merchant_instances"
6988 : " WHERE merchant_id=$1",
6989 : 5),
6990 : /* for postgres_delete_instance_private_key() */
6991 0 : GNUNET_PQ_make_prepare ("delete_key",
6992 : "DELETE FROM merchant_keys"
6993 : " USING merchant_instances"
6994 : " WHERE merchant_keys.merchant_serial"
6995 : " = merchant_instances.merchant_serial"
6996 : " AND merchant_instances.merchant_id = $1",
6997 : 1),
6998 : /* for postgres_purge_instance() */
6999 0 : GNUNET_PQ_make_prepare ("purge_instance",
7000 : "DELETE FROM merchant_instances"
7001 : " WHERE merchant_instances.merchant_id = $1",
7002 : 1),
7003 : /* for postgres_update_instance() */
7004 0 : GNUNET_PQ_make_prepare ("update_instance",
7005 : "UPDATE merchant_instances SET"
7006 : " merchant_name=$2"
7007 : ",address=$3"
7008 : ",jurisdiction=$4"
7009 : ",default_max_deposit_fee_val=$5"
7010 : ",default_max_deposit_fee_frac=$6"
7011 : ",default_max_wire_fee_val=$7"
7012 : ",default_max_wire_fee_frac=$8"
7013 : ",default_wire_fee_amortization=$9"
7014 : ",default_wire_transfer_delay=$10"
7015 : ",default_pay_delay=$11"
7016 : ",website=$12"
7017 : ",email=$13"
7018 : ",logo=$14"
7019 : " WHERE merchant_id = $1",
7020 : 11),
7021 : /* for postgres_update_instance_auth() */
7022 0 : GNUNET_PQ_make_prepare ("update_instance_auth",
7023 : "UPDATE merchant_instances SET"
7024 : " auth_hash=$2"
7025 : ",auth_salt=$3"
7026 : " WHERE merchant_id=$1",
7027 : 3),
7028 : /* for postgres_inactivate_account(); the merchant
7029 : instance is implied from the random salt that
7030 : is part of the h_wire calculation */
7031 0 : GNUNET_PQ_make_prepare ("inactivate_account",
7032 : "UPDATE merchant_accounts SET"
7033 : " active=FALSE"
7034 : " WHERE h_wire=$2 AND"
7035 : " merchant_serial="
7036 : " (SELECT merchant_serial"
7037 : " FROM merchant_instances"
7038 : " WHERE merchant_id=$1)",
7039 : 2),
7040 : /* for postgres_activate_account() */
7041 0 : GNUNET_PQ_make_prepare ("activate_account",
7042 : "UPDATE merchant_accounts SET"
7043 : " active=TRUE"
7044 : " WHERE h_wire=$2 AND"
7045 : " merchant_serial="
7046 : " (SELECT merchant_serial"
7047 : " FROM merchant_instances"
7048 : " WHERE merchant_id=$1)",
7049 : 2),
7050 : /* for postgres_lookup_products() */
7051 0 : GNUNET_PQ_make_prepare ("lookup_products",
7052 : "SELECT"
7053 : " product_id"
7054 : " FROM merchant_inventory"
7055 : " JOIN merchant_instances"
7056 : " USING (merchant_serial)"
7057 : " WHERE merchant_instances.merchant_id=$1",
7058 : 1),
7059 : /* for postgres_lookup_product() */
7060 0 : GNUNET_PQ_make_prepare ("lookup_product",
7061 : "SELECT"
7062 : " description"
7063 : ",description_i18n"
7064 : ",unit"
7065 : ",price_val"
7066 : ",price_frac"
7067 : ",taxes"
7068 : ",total_stock"
7069 : ",total_sold"
7070 : ",total_lost"
7071 : ",image"
7072 : ",merchant_inventory.address"
7073 : ",next_restock"
7074 : ",minimum_age"
7075 : " FROM merchant_inventory"
7076 : " JOIN merchant_instances"
7077 : " USING (merchant_serial)"
7078 : " WHERE merchant_instances.merchant_id=$1"
7079 : " AND merchant_inventory.product_id=$2",
7080 : 2),
7081 : /* for postgres_delete_product() */
7082 0 : GNUNET_PQ_make_prepare ("delete_product",
7083 : "DELETE"
7084 : " FROM merchant_inventory"
7085 : " WHERE merchant_inventory.merchant_serial="
7086 : " (SELECT merchant_serial "
7087 : " FROM merchant_instances"
7088 : " WHERE merchant_id=$1)"
7089 : " AND merchant_inventory.product_id=$2"
7090 : " AND product_serial NOT IN "
7091 : " (SELECT product_serial FROM merchant_order_locks)"
7092 : " AND product_serial NOT IN "
7093 : " (SELECT product_serial FROM merchant_inventory_locks)",
7094 : 2),
7095 : /* for postgres_insert_product() */
7096 0 : GNUNET_PQ_make_prepare ("insert_product",
7097 : "INSERT INTO merchant_inventory"
7098 : "(merchant_serial"
7099 : ",product_id"
7100 : ",description"
7101 : ",description_i18n"
7102 : ",unit"
7103 : ",image"
7104 : ",taxes"
7105 : ",price_val"
7106 : ",price_frac"
7107 : ",total_stock"
7108 : ",address"
7109 : ",next_restock"
7110 : ",minimum_age"
7111 : ")"
7112 : " SELECT merchant_serial,"
7113 : " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13"
7114 : " FROM merchant_instances"
7115 : " WHERE merchant_id=$1",
7116 : 13),
7117 : /* for postgres_update_product() */
7118 0 : GNUNET_PQ_make_prepare ("update_product",
7119 : "UPDATE merchant_inventory SET"
7120 : " description=$3"
7121 : ",description_i18n=$4"
7122 : ",unit=$5"
7123 : ",image=$6"
7124 : ",taxes=$7"
7125 : ",price_val=$8"
7126 : ",price_frac=$9"
7127 : ",total_stock=$10"
7128 : ",total_lost=$11"
7129 : ",address=$12"
7130 : ",next_restock=$13"
7131 : ",minimum_age=$14"
7132 : " WHERE merchant_serial="
7133 : " (SELECT merchant_serial"
7134 : " FROM merchant_instances"
7135 : " WHERE merchant_id=$1)"
7136 : " AND product_id=$2"
7137 : " AND total_stock <= $10"
7138 : " AND total_lost <= $11",
7139 : 14),
7140 :
7141 : /* for postgres_lock_product() */
7142 0 : GNUNET_PQ_make_prepare ("lock_product",
7143 : "WITH ps AS"
7144 : " (SELECT product_serial"
7145 : " FROM merchant_inventory"
7146 : " WHERE product_id=$2"
7147 : " AND merchant_serial="
7148 : " (SELECT merchant_serial"
7149 : " FROM merchant_instances"
7150 : " WHERE merchant_id=$1))"
7151 : "INSERT INTO merchant_inventory_locks"
7152 : "(product_serial"
7153 : ",lock_uuid"
7154 : ",total_locked"
7155 : ",expiration)"
7156 : " SELECT product_serial, $3, $4, $5"
7157 : " FROM merchant_inventory"
7158 : " JOIN ps USING (product_serial)"
7159 : " WHERE "
7160 : " total_stock - total_sold - total_lost - $4 >= "
7161 : " (SELECT COALESCE(SUM(total_locked), 0)"
7162 : " FROM merchant_inventory_locks"
7163 : " WHERE product_serial=ps.product_serial) + "
7164 : " (SELECT COALESCE(SUM(total_locked), 0)"
7165 : " FROM merchant_order_locks"
7166 : " WHERE product_serial=ps.product_serial)",
7167 : 5),
7168 :
7169 : /* for postgres_expire_locks() */
7170 0 : GNUNET_PQ_make_prepare ("unlock_products",
7171 : "DELETE FROM merchant_inventory_locks"
7172 : " WHERE expiration < $1",
7173 : 1),
7174 : /* for postgres_expire_locks() */
7175 0 : GNUNET_PQ_make_prepare ("unlock_orders",
7176 : "DELETE FROM merchant_orders"
7177 : " WHERE pay_deadline < $1",
7178 : 1),
7179 : /* for postgres_expire_locks() */
7180 0 : GNUNET_PQ_make_prepare ("unlock_contracts",
7181 : "DELETE FROM merchant_contract_terms"
7182 : " WHERE NOT paid"
7183 : " AND pay_deadline < $1",
7184 : 1),
7185 :
7186 : /* for postgres_delete_order() */
7187 0 : GNUNET_PQ_make_prepare ("delete_order",
7188 : "WITH ms AS"
7189 : "(SELECT merchant_serial "
7190 : " FROM merchant_instances"
7191 : " WHERE merchant_id=$1)"
7192 : ", mc AS"
7193 : "(SELECT paid"
7194 : " FROM merchant_contract_terms"
7195 : " JOIN ms USING (merchant_serial)"
7196 : " WHERE order_id=$2) "
7197 : "DELETE"
7198 : " FROM merchant_orders mo"
7199 : " WHERE order_id=$2"
7200 : " AND merchant_serial=(SELECT merchant_serial FROM ms)"
7201 : " AND ( (pay_deadline < $3)"
7202 : " OR (NOT EXISTS (SELECT paid FROM mc))"
7203 : " OR ($4 AND (FALSE=(SELECT paid FROM mc))) );",
7204 : 4),
7205 0 : GNUNET_PQ_make_prepare ("delete_contract",
7206 : "DELETE"
7207 : " FROM merchant_contract_terms"
7208 : " WHERE order_id=$2 AND"
7209 : " merchant_serial="
7210 : " (SELECT merchant_serial "
7211 : " FROM merchant_instances"
7212 : " WHERE merchant_id=$1)"
7213 : " AND NOT paid;",
7214 : 2),
7215 : /* for postgres_lookup_order() */
7216 0 : GNUNET_PQ_make_prepare ("lookup_order",
7217 : "SELECT"
7218 : " contract_terms"
7219 : ",claim_token"
7220 : ",h_post_data"
7221 : " FROM merchant_orders"
7222 : " WHERE merchant_orders.merchant_serial="
7223 : " (SELECT merchant_serial "
7224 : " FROM merchant_instances"
7225 : " WHERE merchant_id=$1)"
7226 : " AND merchant_orders.order_id=$2",
7227 : 2),
7228 : /* for postgres_lookup_order_summary() */
7229 0 : GNUNET_PQ_make_prepare ("lookup_order_summary",
7230 : "(SELECT"
7231 : " creation_time"
7232 : ",order_serial"
7233 : " FROM merchant_contract_terms"
7234 : " WHERE merchant_contract_terms.merchant_serial="
7235 : " (SELECT merchant_serial "
7236 : " FROM merchant_instances"
7237 : " WHERE merchant_id=$1)"
7238 : " AND merchant_contract_terms.order_id=$2)"
7239 : "UNION"
7240 : "(SELECT"
7241 : " creation_time"
7242 : ",order_serial"
7243 : " FROM merchant_orders"
7244 : " WHERE merchant_orders.merchant_serial="
7245 : " (SELECT merchant_serial "
7246 : " FROM merchant_instances"
7247 : " WHERE merchant_id=$1)"
7248 : " AND merchant_orders.order_id=$2)",
7249 : 2),
7250 : /* for postgres_lookup_orders() */
7251 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc",
7252 : "(SELECT"
7253 : " order_id"
7254 : ",order_serial"
7255 : ",creation_time"
7256 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7257 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7258 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7259 : " FROM merchant_orders"
7260 : " WHERE merchant_orders.merchant_serial="
7261 : " (SELECT merchant_serial "
7262 : " FROM merchant_instances"
7263 : " WHERE merchant_id=$1)"
7264 : " AND"
7265 : " order_serial > $3"
7266 : " AND"
7267 : " creation_time > $4"
7268 : " ORDER BY order_serial ASC"
7269 : " LIMIT $2)"
7270 : "UNION " /* union ensures elements are distinct! */
7271 : "(SELECT"
7272 : " order_id"
7273 : ",order_serial"
7274 : ",creation_time"
7275 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7276 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7277 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7278 : " FROM merchant_contract_terms"
7279 : " WHERE merchant_contract_terms.merchant_serial="
7280 : " (SELECT merchant_serial "
7281 : " FROM merchant_instances"
7282 : " WHERE merchant_id=$1)"
7283 : " AND"
7284 : " order_serial > $3"
7285 : " AND"
7286 : " creation_time > $4"
7287 : " ORDER BY order_serial ASC"
7288 : " LIMIT $2)"
7289 : " ORDER BY order_serial ASC"
7290 : " LIMIT $2",
7291 : 7),
7292 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid",
7293 : "(SELECT"
7294 : " order_id"
7295 : ",order_serial"
7296 : ",creation_time"
7297 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7298 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7299 : " FROM merchant_orders"
7300 : " WHERE merchant_orders.merchant_serial="
7301 : " (SELECT merchant_serial "
7302 : " FROM merchant_instances"
7303 : " WHERE merchant_id=$1)"
7304 : " AND"
7305 : " order_serial > $3"
7306 : " AND"
7307 : " creation_time > $4"
7308 : " AND"
7309 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7310 : " AND"
7311 : " order_serial NOT IN"
7312 : " (SELECT order_serial"
7313 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7314 : " ORDER BY order_serial ASC"
7315 : " LIMIT $2)"
7316 : "UNION " /* union ensures elements are distinct! */
7317 : "(SELECT"
7318 : " order_id"
7319 : ",order_serial"
7320 : ",creation_time"
7321 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7322 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7323 : " FROM merchant_contract_terms"
7324 : " WHERE merchant_contract_terms.merchant_serial="
7325 : " (SELECT merchant_serial "
7326 : " FROM merchant_instances"
7327 : " WHERE merchant_id=$1)"
7328 : " AND"
7329 : " order_serial > $3"
7330 : " AND"
7331 : " creation_time > $4"
7332 : " AND"
7333 : " BOOL($5) = paid"
7334 : " ORDER BY order_serial ASC"
7335 : " LIMIT $2)"
7336 : " ORDER BY order_serial ASC"
7337 : " LIMIT $2",
7338 : 7),
7339 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded",
7340 : "(SELECT"
7341 : " order_id"
7342 : ",order_serial"
7343 : ",creation_time"
7344 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7345 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7346 : " FROM merchant_orders"
7347 : " WHERE merchant_orders.merchant_serial="
7348 : " (SELECT merchant_serial "
7349 : " FROM merchant_instances"
7350 : " WHERE merchant_id=$1)"
7351 : " AND"
7352 : " order_serial > $3"
7353 : " AND"
7354 : " creation_time > $4"
7355 : " AND"
7356 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7357 : " AND"
7358 : " order_serial NOT IN"
7359 : " (SELECT order_serial"
7360 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7361 : " ORDER BY order_serial ASC"
7362 : " LIMIT $2)"
7363 : "UNION " /* union ensures elements are distinct! */
7364 : "(SELECT"
7365 : " order_id"
7366 : ",order_serial"
7367 : ",creation_time"
7368 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7369 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7370 : " FROM merchant_contract_terms"
7371 : " WHERE merchant_contract_terms.merchant_serial="
7372 : " (SELECT merchant_serial "
7373 : " FROM merchant_instances"
7374 : " WHERE merchant_id=$1)"
7375 : " AND"
7376 : " order_serial > $3"
7377 : " AND"
7378 : " creation_time > $4"
7379 : " AND"
7380 : " CAST($6 as BOOL) = (order_serial IN"
7381 : " (SELECT order_serial "
7382 : " FROM merchant_refunds))"
7383 : " ORDER BY order_serial ASC"
7384 : " LIMIT $2)"
7385 : " ORDER BY order_serial ASC"
7386 : " LIMIT $2",
7387 : 7),
7388 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_wired",
7389 : "(SELECT"
7390 : " order_id"
7391 : ",order_serial"
7392 : ",creation_time"
7393 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7394 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7395 : " FROM merchant_orders"
7396 : " WHERE merchant_orders.merchant_serial="
7397 : " (SELECT merchant_serial "
7398 : " FROM merchant_instances"
7399 : " WHERE merchant_id=$1)"
7400 : " AND"
7401 : " order_serial > $3"
7402 : " AND"
7403 : " creation_time > $4"
7404 : " AND"
7405 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7406 : " AND"
7407 : " order_serial NOT IN"
7408 : " (SELECT order_serial"
7409 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7410 : " ORDER BY order_serial ASC"
7411 : " LIMIT $2)"
7412 : "UNION " /* union ensures elements are distinct! */
7413 : "(SELECT"
7414 : " order_id"
7415 : ",order_serial"
7416 : ",creation_time"
7417 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7418 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7419 : " FROM merchant_contract_terms"
7420 : " WHERE merchant_contract_terms.merchant_serial="
7421 : " (SELECT merchant_serial "
7422 : " FROM merchant_instances"
7423 : " WHERE merchant_id=$1)"
7424 : " AND"
7425 : " order_serial > $3"
7426 : " AND"
7427 : " creation_time > $4"
7428 : " AND"
7429 : " BOOL($7) = wired"
7430 : " ORDER BY order_serial ASC"
7431 : " LIMIT $2)"
7432 : " ORDER BY order_serial ASC"
7433 : " LIMIT $2",
7434 : 7),
7435 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded",
7436 : "(SELECT"
7437 : " order_id"
7438 : ",order_serial"
7439 : ",creation_time"
7440 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7441 : " FROM merchant_orders"
7442 : " WHERE merchant_orders.merchant_serial="
7443 : " (SELECT merchant_serial "
7444 : " FROM merchant_instances"
7445 : " WHERE merchant_id=$1)"
7446 : " AND"
7447 : " order_serial > $3"
7448 : " AND"
7449 : " creation_time > $4"
7450 : " AND"
7451 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7452 : " AND"
7453 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7454 : " AND"
7455 : " order_serial NOT IN"
7456 : " (SELECT order_serial"
7457 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7458 : " ORDER BY order_serial ASC"
7459 : " LIMIT $2)"
7460 : "UNION " /* union ensures elements are distinct! */
7461 : "(SELECT"
7462 : " order_id"
7463 : ",order_serial"
7464 : ",creation_time"
7465 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7466 : " FROM merchant_contract_terms"
7467 : " WHERE merchant_contract_terms.merchant_serial="
7468 : " (SELECT merchant_serial "
7469 : " FROM merchant_instances"
7470 : " WHERE merchant_id=$1)"
7471 : " AND"
7472 : " order_serial > $3"
7473 : " AND"
7474 : " creation_time > $4"
7475 : " AND"
7476 : " BOOL($5) = paid"
7477 : " AND"
7478 : " BOOL($6) = (order_serial IN"
7479 : " (SELECT order_serial "
7480 : " FROM merchant_refunds))"
7481 : " ORDER BY order_serial ASC"
7482 : " LIMIT $2)"
7483 : " ORDER BY order_serial ASC"
7484 : " LIMIT $2",
7485 : 7),
7486 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_wired",
7487 : "(SELECT"
7488 : " order_id"
7489 : ",order_serial"
7490 : ",creation_time"
7491 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7492 : " FROM merchant_orders"
7493 : " WHERE merchant_orders.merchant_serial="
7494 : " (SELECT merchant_serial "
7495 : " FROM merchant_instances"
7496 : " WHERE merchant_id=$1)"
7497 : " AND"
7498 : " order_serial > $3"
7499 : " AND"
7500 : " creation_time > $4"
7501 : " AND"
7502 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7503 : " AND"
7504 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7505 : " AND"
7506 : " order_serial NOT IN"
7507 : " (SELECT order_serial"
7508 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7509 : " ORDER BY order_serial ASC"
7510 : " LIMIT $2)"
7511 : "UNION " /* union ensures elements are distinct! */
7512 : "(SELECT"
7513 : " order_id"
7514 : ",order_serial"
7515 : ",creation_time"
7516 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7517 : " FROM merchant_contract_terms"
7518 : " WHERE merchant_contract_terms.merchant_serial="
7519 : " (SELECT merchant_serial "
7520 : " FROM merchant_instances"
7521 : " WHERE merchant_id=$1)"
7522 : " AND"
7523 : " order_serial > $3"
7524 : " AND"
7525 : " creation_time > $4"
7526 : " AND"
7527 : " BOOL($5) = paid"
7528 : " AND"
7529 : " BOOL($7) = wired"
7530 : " ORDER BY order_serial ASC"
7531 : " LIMIT $2)"
7532 : " ORDER BY order_serial ASC"
7533 : " LIMIT $2",
7534 : 7),
7535 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded_wired",
7536 : "(SELECT"
7537 : " order_id"
7538 : ",order_serial"
7539 : ",creation_time"
7540 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7541 : " FROM merchant_orders"
7542 : " WHERE merchant_orders.merchant_serial="
7543 : " (SELECT merchant_serial "
7544 : " FROM merchant_instances"
7545 : " WHERE merchant_id=$1)"
7546 : " AND"
7547 : " order_serial > $3"
7548 : " AND"
7549 : " creation_time > $4"
7550 : " AND"
7551 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7552 : " AND"
7553 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7554 : " AND"
7555 : " order_serial NOT IN"
7556 : " (SELECT order_serial"
7557 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7558 : " ORDER BY order_serial ASC"
7559 : " LIMIT $2)"
7560 : "UNION " /* union ensures elements are distinct! */
7561 : "(SELECT"
7562 : " order_id"
7563 : ",order_serial"
7564 : ",creation_time"
7565 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7566 : " FROM merchant_contract_terms"
7567 : " WHERE merchant_contract_terms.merchant_serial="
7568 : " (SELECT merchant_serial "
7569 : " FROM merchant_instances"
7570 : " WHERE merchant_id=$1)"
7571 : " AND"
7572 : " order_serial > $3"
7573 : " AND"
7574 : " creation_time > $4"
7575 : " AND"
7576 : " BOOL($6) = (order_serial IN"
7577 : " (SELECT order_serial "
7578 : " FROM merchant_refunds))"
7579 : " AND"
7580 : " BOOL($7) = wired"
7581 : " ORDER BY order_serial ASC"
7582 : " LIMIT $2)"
7583 : " ORDER BY order_serial ASC"
7584 : " LIMIT $2",
7585 : 7),
7586 0 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded_wired",
7587 : "(SELECT"
7588 : " order_id"
7589 : ",order_serial"
7590 : ",creation_time"
7591 : " FROM merchant_orders"
7592 : " WHERE merchant_orders.merchant_serial="
7593 : " (SELECT merchant_serial "
7594 : " FROM merchant_instances"
7595 : " WHERE merchant_id=$1)"
7596 : " AND"
7597 : " order_serial > $3"
7598 : " AND"
7599 : " creation_time > $4"
7600 : " AND"
7601 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7602 : " AND"
7603 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7604 : " AND"
7605 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7606 : " AND"
7607 : " order_serial NOT IN"
7608 : " (SELECT order_serial"
7609 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7610 : " ORDER BY order_serial ASC"
7611 : " LIMIT $2)"
7612 : "UNION " /* union ensures elements are distinct! */
7613 : "(SELECT"
7614 : " order_id"
7615 : ",order_serial"
7616 : ",creation_time"
7617 : " FROM merchant_contract_terms"
7618 : " WHERE merchant_contract_terms.merchant_serial="
7619 : " (SELECT merchant_serial "
7620 : " FROM merchant_instances"
7621 : " WHERE merchant_id=$1)"
7622 : " AND"
7623 : " order_serial > $3"
7624 : " AND"
7625 : " creation_time > $4"
7626 : " AND"
7627 : " BOOL($5) = paid"
7628 : " AND"
7629 : " BOOL($6) = (order_serial IN"
7630 : " (SELECT order_serial "
7631 : " FROM merchant_refunds))"
7632 : " AND"
7633 : " BOOL($7) = wired"
7634 : " ORDER BY order_serial ASC"
7635 : " LIMIT $2)"
7636 : " ORDER BY order_serial ASC"
7637 : " LIMIT $2",
7638 : 7),
7639 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec",
7640 : "(SELECT"
7641 : " order_id"
7642 : ",order_serial"
7643 : ",creation_time"
7644 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7645 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7646 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7647 : " FROM merchant_orders"
7648 : " WHERE merchant_orders.merchant_serial="
7649 : " (SELECT merchant_serial "
7650 : " FROM merchant_instances"
7651 : " WHERE merchant_id=$1)"
7652 : " AND"
7653 : " order_serial < $3"
7654 : " AND"
7655 : " creation_time < $4"
7656 : " ORDER BY order_serial DESC"
7657 : " LIMIT $2)"
7658 : "UNION " /* union ensures elements are distinct! */
7659 : "(SELECT"
7660 : " order_id"
7661 : ",order_serial"
7662 : ",creation_time"
7663 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7664 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7665 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7666 : " FROM merchant_contract_terms"
7667 : " WHERE merchant_contract_terms.merchant_serial="
7668 : " (SELECT merchant_serial "
7669 : " FROM merchant_instances"
7670 : " WHERE merchant_id=$1)"
7671 : " AND"
7672 : " order_serial < $3"
7673 : " AND"
7674 : " creation_time < $4"
7675 : " ORDER BY order_serial DESC"
7676 : " LIMIT $2)"
7677 : " ORDER BY order_serial DESC"
7678 : " LIMIT $2",
7679 : 7),
7680 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid",
7681 : "(SELECT"
7682 : " order_id"
7683 : ",order_serial"
7684 : ",creation_time"
7685 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7686 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7687 : " FROM merchant_orders"
7688 : " WHERE merchant_orders.merchant_serial="
7689 : " (SELECT merchant_serial "
7690 : " FROM merchant_instances"
7691 : " WHERE merchant_id=$1)"
7692 : " AND"
7693 : " order_serial < $3"
7694 : " AND"
7695 : " creation_time < $4"
7696 : " AND"
7697 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7698 : " AND"
7699 : " order_serial NOT IN"
7700 : " (SELECT order_serial"
7701 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7702 : " ORDER BY order_serial DESC"
7703 : " LIMIT $2)"
7704 : "UNION " /* union ensures elements are distinct! */
7705 : "(SELECT"
7706 : " order_id"
7707 : ",order_serial"
7708 : ",creation_time"
7709 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7710 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7711 : " FROM merchant_contract_terms"
7712 : " WHERE merchant_contract_terms.merchant_serial="
7713 : " (SELECT merchant_serial "
7714 : " FROM merchant_instances"
7715 : " WHERE merchant_id=$1)"
7716 : " AND"
7717 : " order_serial < $3"
7718 : " AND"
7719 : " creation_time < $4"
7720 : " AND"
7721 : " BOOL($5) = paid"
7722 : " ORDER BY order_serial DESC"
7723 : " LIMIT $2)"
7724 : " ORDER BY order_serial DESC"
7725 : " LIMIT $2",
7726 : 7),
7727 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded",
7728 : "(SELECT"
7729 : " order_id"
7730 : ",order_serial"
7731 : ",creation_time"
7732 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7733 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7734 : " FROM merchant_orders"
7735 : " WHERE merchant_orders.merchant_serial="
7736 : " (SELECT merchant_serial "
7737 : " FROM merchant_instances"
7738 : " WHERE merchant_id=$1)"
7739 : " AND"
7740 : " order_serial < $3"
7741 : " AND"
7742 : " creation_time < $4"
7743 : " AND"
7744 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7745 : " AND"
7746 : " order_serial NOT IN"
7747 : " (SELECT order_serial"
7748 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7749 : " ORDER BY order_serial DESC"
7750 : " LIMIT $2)"
7751 : "UNION " /* union ensures elements are distinct! */
7752 : "(SELECT"
7753 : " order_id"
7754 : ",order_serial"
7755 : ",creation_time"
7756 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7757 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7758 : " FROM merchant_contract_terms"
7759 : " WHERE merchant_contract_terms.merchant_serial="
7760 : " (SELECT merchant_serial "
7761 : " FROM merchant_instances"
7762 : " WHERE merchant_id=$1)"
7763 : " AND"
7764 : " order_serial < $3"
7765 : " AND"
7766 : " creation_time < $4"
7767 : " AND"
7768 : " BOOL($6) = (order_serial IN"
7769 : " (SELECT order_serial "
7770 : " FROM merchant_refunds))"
7771 : " ORDER BY order_serial DESC"
7772 : " LIMIT $2)"
7773 : " ORDER BY order_serial DESC"
7774 : " LIMIT $2",
7775 : 7),
7776 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_wired",
7777 : "(SELECT"
7778 : " order_id"
7779 : ",order_serial"
7780 : ",creation_time"
7781 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7782 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7783 : " FROM merchant_orders"
7784 : " WHERE merchant_orders.merchant_serial="
7785 : " (SELECT merchant_serial "
7786 : " FROM merchant_instances"
7787 : " WHERE merchant_id=$1)"
7788 : " AND"
7789 : " order_serial < $3"
7790 : " AND"
7791 : " creation_time < $4"
7792 : " AND"
7793 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7794 : " AND"
7795 : " order_serial NOT IN"
7796 : " (SELECT order_serial"
7797 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7798 : " ORDER BY order_serial DESC"
7799 : " LIMIT $2)"
7800 : "UNION " /* union ensures elements are distinct! */
7801 : "(SELECT"
7802 : " order_id"
7803 : ",order_serial"
7804 : ",creation_time"
7805 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7806 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7807 : " FROM merchant_contract_terms"
7808 : " WHERE merchant_contract_terms.merchant_serial="
7809 : " (SELECT merchant_serial "
7810 : " FROM merchant_instances"
7811 : " WHERE merchant_id=$1)"
7812 : " AND"
7813 : " order_serial < $3"
7814 : " AND"
7815 : " creation_time < $4"
7816 : " AND"
7817 : " BOOL($7) = wired"
7818 : " ORDER BY order_serial DESC"
7819 : " LIMIT $2)"
7820 : " ORDER BY order_serial DESC"
7821 : " LIMIT $2",
7822 : 7),
7823 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded",
7824 : "(SELECT"
7825 : " order_id"
7826 : ",order_serial"
7827 : ",creation_time"
7828 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7829 : " FROM merchant_orders"
7830 : " WHERE merchant_orders.merchant_serial="
7831 : " (SELECT merchant_serial "
7832 : " FROM merchant_instances"
7833 : " WHERE merchant_id=$1)"
7834 : " AND"
7835 : " order_serial < $3"
7836 : " AND"
7837 : " creation_time < $4"
7838 : " AND"
7839 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7840 : " AND"
7841 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7842 : " AND"
7843 : " order_serial NOT IN"
7844 : " (SELECT order_serial"
7845 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7846 : " ORDER BY order_serial DESC"
7847 : " LIMIT $2)"
7848 : "UNION " /* union ensures elements are distinct! */
7849 : "(SELECT"
7850 : " order_id"
7851 : ",order_serial"
7852 : ",creation_time"
7853 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7854 : " FROM merchant_contract_terms"
7855 : " WHERE merchant_contract_terms.merchant_serial="
7856 : " (SELECT merchant_serial "
7857 : " FROM merchant_instances"
7858 : " WHERE merchant_id=$1)"
7859 : " AND"
7860 : " order_serial < $3"
7861 : " AND"
7862 : " creation_time < $4"
7863 : " AND"
7864 : " BOOL($5) = paid"
7865 : " AND"
7866 : " BOOL($6) = (order_serial IN"
7867 : " (SELECT order_serial "
7868 : " FROM merchant_refunds))"
7869 : " ORDER BY order_serial DESC"
7870 : " LIMIT $2)"
7871 : " ORDER BY order_serial DESC"
7872 : " LIMIT $2",
7873 : 7),
7874 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_wired",
7875 : "(SELECT"
7876 : " order_id"
7877 : ",order_serial"
7878 : ",creation_time"
7879 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7880 : " FROM merchant_orders"
7881 : " WHERE merchant_orders.merchant_serial="
7882 : " (SELECT merchant_serial "
7883 : " FROM merchant_instances"
7884 : " WHERE merchant_id=$1)"
7885 : " AND"
7886 : " order_serial < $3"
7887 : " AND"
7888 : " creation_time < $4"
7889 : " AND"
7890 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7891 : " AND"
7892 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7893 : " AND"
7894 : " order_serial NOT IN"
7895 : " (SELECT order_serial"
7896 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7897 : " ORDER BY order_serial DESC"
7898 : " LIMIT $2)"
7899 : "UNION " /* union ensures elements are distinct! */
7900 : "(SELECT"
7901 : " order_id"
7902 : ",order_serial"
7903 : ",creation_time"
7904 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7905 : " FROM merchant_contract_terms"
7906 : " WHERE merchant_contract_terms.merchant_serial="
7907 : " (SELECT merchant_serial "
7908 : " FROM merchant_instances"
7909 : " WHERE merchant_id=$1)"
7910 : " AND"
7911 : " order_serial < $3"
7912 : " AND"
7913 : " creation_time < $4"
7914 : " AND"
7915 : " BOOL($5) = paid"
7916 : " AND"
7917 : " BOOL($7) = wired"
7918 : " ORDER BY order_serial DESC"
7919 : " LIMIT $2)"
7920 : " ORDER BY order_serial DESC"
7921 : " LIMIT $2",
7922 : 7),
7923 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded_wired",
7924 : "(SELECT"
7925 : " order_id"
7926 : ",order_serial"
7927 : ",creation_time"
7928 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7929 : " FROM merchant_orders"
7930 : " WHERE merchant_orders.merchant_serial="
7931 : " (SELECT merchant_serial "
7932 : " FROM merchant_instances"
7933 : " WHERE merchant_id=$1)"
7934 : " AND"
7935 : " order_serial < $3"
7936 : " AND"
7937 : " creation_time < $4"
7938 : " AND"
7939 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7940 : " AND"
7941 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7942 : " AND"
7943 : " order_serial NOT IN"
7944 : " (SELECT order_serial"
7945 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7946 : " ORDER BY order_serial DESC"
7947 : " LIMIT $2)"
7948 : "UNION " /* union ensures elements are distinct! */
7949 : "(SELECT"
7950 : " order_id"
7951 : ",order_serial"
7952 : ",creation_time"
7953 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7954 : " FROM merchant_contract_terms"
7955 : " WHERE merchant_contract_terms.merchant_serial="
7956 : " (SELECT merchant_serial "
7957 : " FROM merchant_instances"
7958 : " WHERE merchant_id=$1)"
7959 : " AND"
7960 : " order_serial < $3"
7961 : " AND"
7962 : " creation_time < $4"
7963 : " AND"
7964 : " BOOL($6) = (order_serial IN"
7965 : " (SELECT order_serial "
7966 : " FROM merchant_refunds))"
7967 : " AND"
7968 : " BOOL($7) = wired"
7969 : " ORDER BY order_serial DESC"
7970 : " LIMIT $2)"
7971 : " ORDER BY order_serial DESC"
7972 : " LIMIT $2",
7973 : 7),
7974 0 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded_wired",
7975 : "(SELECT"
7976 : " order_id"
7977 : ",order_serial"
7978 : ",creation_time"
7979 : " FROM merchant_orders"
7980 : " WHERE merchant_orders.merchant_serial="
7981 : " (SELECT merchant_serial "
7982 : " FROM merchant_instances"
7983 : " WHERE merchant_id=$1)"
7984 : " AND"
7985 : " order_serial < $3"
7986 : " AND"
7987 : " creation_time < $4"
7988 : " AND"
7989 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7990 : " AND"
7991 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7992 : " AND"
7993 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7994 : " AND"
7995 : " order_serial NOT IN"
7996 : " (SELECT order_serial"
7997 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7998 : " ORDER BY order_serial DESC"
7999 : " LIMIT $2)"
8000 : "UNION " /* union ensures elements are distinct! */
8001 : "(SELECT"
8002 : " order_id"
8003 : ",order_serial"
8004 : ",creation_time"
8005 : " FROM merchant_contract_terms"
8006 : " WHERE merchant_contract_terms.merchant_serial="
8007 : " (SELECT merchant_serial "
8008 : " FROM merchant_instances"
8009 : " WHERE merchant_id=$1)"
8010 : " AND"
8011 : " order_serial < $3"
8012 : " AND"
8013 : " creation_time < $4"
8014 : " AND"
8015 : " BOOL($5) = paid"
8016 : " AND"
8017 : " BOOL($6) = (order_serial IN"
8018 : " (SELECT order_serial "
8019 : " FROM merchant_refunds))"
8020 : " AND"
8021 : " BOOL($7) = wired"
8022 : " ORDER BY order_serial DESC"
8023 : " LIMIT $2)"
8024 : " ORDER BY order_serial DESC"
8025 : " LIMIT $2",
8026 : 7),
8027 : /* for postgres_insert_order() */
8028 0 : GNUNET_PQ_make_prepare ("insert_order",
8029 : "INSERT INTO merchant_orders"
8030 : "(merchant_serial"
8031 : ",order_id"
8032 : ",pay_deadline"
8033 : ",claim_token"
8034 : ",h_post_data"
8035 : ",creation_time"
8036 : ",contract_terms)"
8037 : " SELECT merchant_serial,"
8038 : " $2, $3, $4, $5, $6, $7"
8039 : " FROM merchant_instances"
8040 : " WHERE merchant_id=$1",
8041 : 7),
8042 : /* for postgres_unlock_inventory() */
8043 0 : GNUNET_PQ_make_prepare ("unlock_inventory",
8044 : "DELETE"
8045 : " FROM merchant_inventory_locks"
8046 : " WHERE lock_uuid=$1",
8047 : 1),
8048 : /* for postgres_insert_order_lock() */
8049 0 : GNUNET_PQ_make_prepare ("insert_order_lock",
8050 : "WITH tmp AS"
8051 : " (SELECT "
8052 : " product_serial"
8053 : " ,merchant_serial"
8054 : " ,total_stock"
8055 : " ,total_sold"
8056 : " ,total_lost"
8057 : " FROM merchant_inventory"
8058 : " WHERE product_id=$3"
8059 : " AND merchant_serial="
8060 : " (SELECT merchant_serial"
8061 : " FROM merchant_instances"
8062 : " WHERE merchant_id=$1))"
8063 : " INSERT INTO merchant_order_locks"
8064 : " (product_serial"
8065 : " ,total_locked"
8066 : " ,order_serial)"
8067 : " SELECT tmp.product_serial, $4, order_serial"
8068 : " FROM merchant_orders"
8069 : " JOIN tmp USING(merchant_serial)"
8070 : " WHERE order_id=$2 AND"
8071 : " tmp.total_stock - tmp.total_sold - tmp.total_lost - $4 >= "
8072 : " (SELECT COALESCE(SUM(total_locked), 0)"
8073 : " FROM merchant_inventory_locks"
8074 : " WHERE product_serial=tmp.product_serial) + "
8075 : " (SELECT COALESCE(SUM(total_locked), 0)"
8076 : " FROM merchant_order_locks"
8077 : " WHERE product_serial=tmp.product_serial)",
8078 : 4),
8079 : /* for postgres_lookup_contract_terms() */
8080 0 : GNUNET_PQ_make_prepare ("lookup_contract_terms",
8081 : "SELECT"
8082 : " contract_terms"
8083 : ",order_serial"
8084 : ",claim_token"
8085 : ",paid"
8086 : " FROM merchant_contract_terms"
8087 : " WHERE order_id=$2"
8088 : " AND merchant_serial="
8089 : " (SELECT merchant_serial"
8090 : " FROM merchant_instances"
8091 : " WHERE merchant_id=$1)",
8092 : 2),
8093 : /* for postgres_insert_contract_terms() */
8094 0 : GNUNET_PQ_make_prepare ("insert_contract_terms",
8095 : "INSERT INTO merchant_contract_terms"
8096 : "(order_serial"
8097 : ",merchant_serial"
8098 : ",order_id"
8099 : ",contract_terms"
8100 : ",h_contract_terms"
8101 : ",creation_time"
8102 : ",pay_deadline"
8103 : ",refund_deadline"
8104 : ",fulfillment_url"
8105 : ",claim_token)"
8106 : "SELECT"
8107 : " mo.order_serial"
8108 : ",mo.merchant_serial"
8109 : ",mo.order_id"
8110 : ",$3" /* contract_terms */
8111 : ",$4" /* h_contract_terms */
8112 : ",mo.creation_time"
8113 : ",$5" /* pay_deadline */
8114 : ",$6" /* refund_deadline */
8115 : ",$7" /* fulfillment_url */
8116 : ",mo.claim_token "
8117 : "FROM merchant_orders mo"
8118 : " WHERE order_id=$2"
8119 : " AND merchant_serial="
8120 : " (SELECT merchant_serial"
8121 : " FROM merchant_instances"
8122 : " WHERE merchant_id=$1)"
8123 : " RETURNING order_serial",
8124 : 7),
8125 : /* for postgres_update_contract_terms() */
8126 0 : GNUNET_PQ_make_prepare ("update_contract_terms",
8127 : "UPDATE merchant_contract_terms SET"
8128 : " contract_terms=$3"
8129 : ",h_contract_terms=$4"
8130 : ",pay_deadline=$5"
8131 : ",refund_deadline=$6"
8132 : ",fulfillment_url=$7"
8133 : " WHERE order_id=$2"
8134 : " AND merchant_serial="
8135 : " (SELECT merchant_serial"
8136 : " FROM merchant_instances"
8137 : " WHERE merchant_id=$1)",
8138 : 7),
8139 : /* for postgres_delete_contract_terms() */
8140 0 : GNUNET_PQ_make_prepare ("delete_contract_terms",
8141 : "DELETE FROM merchant_contract_terms"
8142 : " WHERE order_id=$2"
8143 : " AND merchant_serial="
8144 : " (SELECT merchant_serial"
8145 : " FROM merchant_instances"
8146 : " WHERE merchant_id=$1)"
8147 : " AND ( ( (pay_deadline < $4) AND"
8148 : " (NOT paid) ) OR"
8149 : " (creation_time + $3 < $4) )",
8150 : 4),
8151 : /* for postgres_lookup_deposits() */
8152 0 : GNUNET_PQ_make_prepare ("lookup_deposits",
8153 : "SELECT"
8154 : " exchange_url"
8155 : ",coin_pub"
8156 : ",amount_with_fee_val"
8157 : ",amount_with_fee_frac"
8158 : ",deposit_fee_val"
8159 : ",deposit_fee_frac"
8160 : ",refund_fee_val"
8161 : ",refund_fee_frac"
8162 : ",wire_fee_val"
8163 : ",wire_fee_frac"
8164 : " FROM merchant_deposits"
8165 : " WHERE order_serial="
8166 : " (SELECT order_serial"
8167 : " FROM merchant_contract_terms"
8168 : " WHERE h_contract_terms=$2"
8169 : " AND merchant_serial="
8170 : " (SELECT merchant_serial"
8171 : " FROM merchant_instances"
8172 : " WHERE merchant_id=$1))",
8173 : 2),
8174 : /* for postgres_insert_exchange_signkey() */
8175 0 : GNUNET_PQ_make_prepare ("insert_exchange_signkey",
8176 : "INSERT INTO merchant_exchange_signing_keys"
8177 : "(master_pub"
8178 : ",exchange_pub"
8179 : ",start_date"
8180 : ",expire_date"
8181 : ",end_date"
8182 : ",master_sig)"
8183 : "VALUES"
8184 : "($1, $2, $3, $4, $5, $6)",
8185 : 6),
8186 : /* for postgres_insert_deposit() */
8187 0 : GNUNET_PQ_make_prepare ("insert_deposit",
8188 : "WITH md AS"
8189 : " (SELECT account_serial, merchant_serial"
8190 : " FROM merchant_accounts"
8191 : " WHERE h_wire=$14"
8192 : " AND merchant_serial="
8193 : " (SELECT merchant_serial"
8194 : " FROM merchant_instances"
8195 : " WHERE merchant_id=$1))"
8196 : ", ed AS"
8197 : " (SELECT signkey_serial"
8198 : " FROM merchant_exchange_signing_keys"
8199 : " WHERE exchange_pub=$16"
8200 : " ORDER BY start_date DESC"
8201 : " LIMIT 1)"
8202 : "INSERT INTO merchant_deposits"
8203 : "(order_serial"
8204 : ",deposit_timestamp"
8205 : ",coin_pub"
8206 : ",exchange_url"
8207 : ",amount_with_fee_val"
8208 : ",amount_with_fee_frac"
8209 : ",deposit_fee_val"
8210 : ",deposit_fee_frac"
8211 : ",refund_fee_val"
8212 : ",refund_fee_frac"
8213 : ",wire_fee_val"
8214 : ",wire_fee_frac"
8215 : ",exchange_sig"
8216 : ",signkey_serial"
8217 : ",account_serial)"
8218 : " SELECT "
8219 : " order_serial"
8220 : " ,$3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $15"
8221 : " ,ed.signkey_serial"
8222 : " ,md.account_serial"
8223 : " FROM merchant_contract_terms"
8224 : " JOIN md USING (merchant_serial)"
8225 : " FULL OUTER JOIN ed ON TRUE"
8226 : " WHERE h_contract_terms=$2",
8227 : 16),
8228 : /* for postgres_lookup_refunds() */
8229 0 : GNUNET_PQ_make_prepare ("lookup_refunds",
8230 : "SELECT"
8231 : " coin_pub"
8232 : ",refund_amount_val"
8233 : ",refund_amount_frac"
8234 : " FROM merchant_refunds"
8235 : " WHERE order_serial="
8236 : " (SELECT order_serial"
8237 : " FROM merchant_contract_terms"
8238 : " WHERE h_contract_terms=$2"
8239 : " AND merchant_serial="
8240 : " (SELECT merchant_serial"
8241 : " FROM merchant_instances"
8242 : " WHERE merchant_id=$1))",
8243 : 2),
8244 : /* for postgres_mark_contract_paid() */
8245 0 : GNUNET_PQ_make_prepare ("mark_contract_paid",
8246 : "UPDATE merchant_contract_terms SET"
8247 : " paid=TRUE"
8248 : ",session_id=$3"
8249 : " WHERE h_contract_terms=$2"
8250 : " AND merchant_serial="
8251 : " (SELECT merchant_serial"
8252 : " FROM merchant_instances"
8253 : " WHERE merchant_id=$1)",
8254 : 3),
8255 : /* for postgres_mark_contract_paid() */
8256 0 : GNUNET_PQ_make_prepare ("mark_inventory_sold",
8257 : "UPDATE merchant_inventory SET"
8258 : " total_sold=total_sold + order_locks.total_locked"
8259 : " FROM (SELECT total_locked,product_serial"
8260 : " FROM merchant_order_locks"
8261 : " WHERE order_serial="
8262 : " (SELECT order_serial"
8263 : " FROM merchant_contract_terms"
8264 : " WHERE h_contract_terms=$2"
8265 : " AND merchant_serial="
8266 : " (SELECT merchant_serial"
8267 : " FROM merchant_instances"
8268 : " WHERE merchant_id=$1))"
8269 : " ) AS order_locks"
8270 : " WHERE merchant_inventory.product_serial"
8271 : " =order_locks.product_serial",
8272 : 2),
8273 : /* for postgres_mark_contract_paid() */
8274 0 : GNUNET_PQ_make_prepare ("delete_completed_order",
8275 : "WITH md AS"
8276 : " (SELECT merchant_serial"
8277 : " FROM merchant_instances"
8278 : " WHERE merchant_id=$1) "
8279 : "DELETE"
8280 : " FROM merchant_orders"
8281 : " WHERE order_serial="
8282 : " (SELECT order_serial"
8283 : " FROM merchant_contract_terms"
8284 : " JOIN md USING (merchant_serial)"
8285 : " WHERE h_contract_terms=$2)",
8286 : 2),
8287 : /* for postgres_refund_coin() */
8288 0 : GNUNET_PQ_make_prepare ("refund_coin",
8289 : "INSERT INTO merchant_refunds"
8290 : "(order_serial"
8291 : ",rtransaction_id"
8292 : ",refund_timestamp"
8293 : ",coin_pub"
8294 : ",reason"
8295 : ",refund_amount_val"
8296 : ",refund_amount_frac"
8297 : ") "
8298 : "SELECT "
8299 : " order_serial"
8300 : ",0" /* rtransaction_id always 0 for /abort */
8301 : ",$3"
8302 : ",coin_pub"
8303 : ",$5"
8304 : ",amount_with_fee_val"
8305 : ",amount_with_fee_frac"
8306 : " FROM merchant_deposits"
8307 : " WHERE coin_pub=$4"
8308 : " AND order_serial="
8309 : " (SELECT order_serial"
8310 : " FROM merchant_contract_terms"
8311 : " WHERE h_contract_terms=$2"
8312 : " AND merchant_serial="
8313 : " (SELECT merchant_serial"
8314 : " FROM merchant_instances"
8315 : " WHERE merchant_id=$1))",
8316 : 5),
8317 :
8318 : /* for postgres_lookup_order_status() */
8319 0 : GNUNET_PQ_make_prepare ("lookup_order_status",
8320 : "SELECT"
8321 : " h_contract_terms"
8322 : ",paid"
8323 : " FROM merchant_contract_terms"
8324 : " WHERE merchant_serial="
8325 : " (SELECT merchant_serial "
8326 : " FROM merchant_instances"
8327 : " WHERE merchant_id=$1)"
8328 : " AND order_id=$2",
8329 : 2),
8330 :
8331 : /* for postgres_lookup_order_status_by_serial() */
8332 0 : GNUNET_PQ_make_prepare ("lookup_order_status_by_serial",
8333 : "SELECT"
8334 : " h_contract_terms"
8335 : ",order_id"
8336 : ",paid"
8337 : " FROM merchant_contract_terms"
8338 : " WHERE merchant_serial="
8339 : " (SELECT merchant_serial "
8340 : " FROM merchant_instances"
8341 : " WHERE merchant_id=$1)"
8342 : " AND order_serial=$2",
8343 : 2),
8344 :
8345 : /* for postgres_lookup_payment_status() */
8346 0 : GNUNET_PQ_make_prepare ("lookup_payment_status",
8347 : "SELECT"
8348 : " wired"
8349 : ",paid"
8350 : " FROM merchant_contract_terms"
8351 : " WHERE order_serial=$1",
8352 : 1),
8353 : /* for postgres_lookup_payment_status() */
8354 0 : GNUNET_PQ_make_prepare ("lookup_payment_status_session_id",
8355 : "SELECT"
8356 : " wired"
8357 : ",paid"
8358 : " FROM merchant_contract_terms"
8359 : " WHERE order_serial=$1"
8360 : " AND session_id=$2",
8361 : 2),
8362 : /* for postgres_lookup_deposits_by_order() */
8363 0 : GNUNET_PQ_make_prepare ("lookup_deposits_by_order",
8364 : "SELECT"
8365 : " deposit_serial"
8366 : ",exchange_url"
8367 : ",h_wire"
8368 : ",amount_with_fee_val"
8369 : ",amount_with_fee_frac"
8370 : ",deposit_fee_val"
8371 : ",deposit_fee_frac"
8372 : ",coin_pub"
8373 : " FROM merchant_deposits"
8374 : " JOIN merchant_accounts USING (account_serial)"
8375 : " WHERE order_serial=$1",
8376 : 1),
8377 : /* for postgres_lookup_transfer_details_by_order() */
8378 0 : GNUNET_PQ_make_prepare ("lookup_transfer_details_by_order",
8379 : "SELECT"
8380 : " md.deposit_serial"
8381 : ",md.exchange_url"
8382 : ",mt.wtid"
8383 : ",exchange_deposit_value_val"
8384 : ",exchange_deposit_value_frac"
8385 : ",exchange_deposit_fee_val"
8386 : ",exchange_deposit_fee_frac"
8387 : ",deposit_timestamp"
8388 : ",mt.confirmed AS transfer_confirmed"
8389 : " FROM merchant_transfer_to_coin"
8390 : " JOIN merchant_deposits AS md USING (deposit_serial)"
8391 : " JOIN merchant_transfers AS mt USING (credit_serial)"
8392 : " WHERE deposit_serial IN"
8393 : " (SELECT deposit_serial"
8394 : " FROM merchant_deposits"
8395 : " WHERE order_serial=$1)",
8396 : 1),
8397 : /* for postgres_insert_deposit_to_transfer() */
8398 0 : GNUNET_PQ_make_prepare ("insert_deposit_to_transfer",
8399 : "INSERT INTO merchant_deposit_to_transfer"
8400 : "(deposit_serial"
8401 : ",coin_contribution_value_val"
8402 : ",coin_contribution_value_frac"
8403 : ",credit_serial"
8404 : ",execution_time"
8405 : ",signkey_serial"
8406 : ",exchange_sig"
8407 : ") SELECT $1, $2, $3, credit_serial, $4, signkey_serial, $5"
8408 : " FROM merchant_transfers"
8409 : " CROSS JOIN merchant_exchange_signing_keys"
8410 : " WHERE exchange_pub=$6"
8411 : " AND wtid=$7",
8412 : 7),
8413 : /* for postgres_mark_order_wired() */
8414 0 : GNUNET_PQ_make_prepare ("mark_order_wired",
8415 : "UPDATE merchant_contract_terms SET"
8416 : " wired=true"
8417 : " WHERE order_serial=$1",
8418 : 1),
8419 : /* for process_refund_cb() used in postgres_increase_refund() */
8420 0 : GNUNET_PQ_make_prepare ("find_refunds_by_coin",
8421 : "SELECT"
8422 : " refund_amount_val"
8423 : ",refund_amount_frac"
8424 : ",rtransaction_id"
8425 : " FROM merchant_refunds"
8426 : " WHERE coin_pub=$1"
8427 : " AND order_serial=$2",
8428 : 2),
8429 : /* for process_deposits_for_refund_cb() used in postgres_increase_refund() */
8430 0 : GNUNET_PQ_make_prepare ("insert_refund",
8431 : "INSERT INTO merchant_refunds"
8432 : "(order_serial"
8433 : ",rtransaction_id"
8434 : ",refund_timestamp"
8435 : ",coin_pub"
8436 : ",reason"
8437 : ",refund_amount_val"
8438 : ",refund_amount_frac"
8439 : ") VALUES"
8440 : "($1, $2, $3, $4, $5, $6, $7)",
8441 : 7),
8442 : /* for postgres_increase_refund() */
8443 0 : GNUNET_PQ_make_prepare ("find_deposits_for_refund",
8444 : "SELECT"
8445 : " coin_pub"
8446 : ",order_serial"
8447 : ",amount_with_fee_val"
8448 : ",amount_with_fee_frac"
8449 : " FROM merchant_deposits"
8450 : " WHERE order_serial="
8451 : " (SELECT order_serial"
8452 : " FROM merchant_contract_terms"
8453 : " WHERE order_id=$2"
8454 : " AND paid=TRUE"
8455 : " AND merchant_serial="
8456 : " (SELECT merchant_serial"
8457 : " FROM merchant_instances"
8458 : " WHERE merchant_id=$1))",
8459 : 2),
8460 : /* for postgres_lookup_refunds_detailed() */
8461 0 : GNUNET_PQ_make_prepare ("lookup_refunds_detailed",
8462 : "SELECT"
8463 : " refund_serial"
8464 : ",refund_timestamp"
8465 : ",coin_pub"
8466 : ",merchant_deposits.exchange_url"
8467 : ",rtransaction_id"
8468 : ",reason"
8469 : ",refund_amount_val"
8470 : ",refund_amount_frac"
8471 : ",merchant_refund_proofs.exchange_sig IS NULL AS pending"
8472 : " FROM merchant_refunds"
8473 : " JOIN merchant_deposits USING (order_serial, coin_pub)"
8474 : " LEFT JOIN merchant_refund_proofs USING (refund_serial)"
8475 : " WHERE order_serial="
8476 : " (SELECT order_serial"
8477 : " FROM merchant_contract_terms"
8478 : " WHERE h_contract_terms=$2"
8479 : " AND merchant_serial="
8480 : " (SELECT merchant_serial"
8481 : " FROM merchant_instances"
8482 : " WHERE merchant_id=$1))",
8483 : 2),
8484 : /* for postgres_insert_refund_proof() */
8485 0 : GNUNET_PQ_make_prepare ("insert_refund_proof",
8486 : "INSERT INTO merchant_refund_proofs"
8487 : "(refund_serial"
8488 : ",exchange_sig"
8489 : ",signkey_serial)"
8490 : "SELECT $1, $2, signkey_serial"
8491 : " FROM merchant_exchange_signing_keys"
8492 : " WHERE exchange_pub=$3"
8493 : " ORDER BY start_date DESC"
8494 : " LIMIT 1",
8495 : 5),
8496 : /* for postgres_lookup_refund_proof() */
8497 0 : GNUNET_PQ_make_prepare ("lookup_refund_proof",
8498 : "SELECT"
8499 : " merchant_exchange_signing_keys.exchange_pub"
8500 : ",exchange_sig"
8501 : " FROM merchant_refund_proofs"
8502 : " JOIN merchant_exchange_signing_keys"
8503 : " USING (signkey_serial)"
8504 : " WHERE"
8505 : " refund_serial=$1",
8506 : 1),
8507 : /* for postgres_lookup_order_by_fulfillment() */
8508 0 : GNUNET_PQ_make_prepare ("lookup_order_by_fulfillment",
8509 : "SELECT"
8510 : " order_id"
8511 : " FROM merchant_contract_terms"
8512 : " WHERE fulfillment_url=$2"
8513 : " AND session_id=$3"
8514 : " AND merchant_serial="
8515 : " (SELECT merchant_serial"
8516 : " FROM merchant_instances"
8517 : " WHERE merchant_id=$1)",
8518 : 3),
8519 : /* for postgres_insert_transfer() */
8520 0 : GNUNET_PQ_make_prepare ("insert_transfer",
8521 : "INSERT INTO merchant_transfers"
8522 : "(exchange_url"
8523 : ",wtid"
8524 : ",credit_amount_val"
8525 : ",credit_amount_frac"
8526 : ",account_serial"
8527 : ",confirmed)"
8528 : "SELECT"
8529 : " $1, $2, $3, $4, account_serial, $6"
8530 : " FROM merchant_accounts"
8531 : " WHERE payto_uri=$5"
8532 : " AND merchant_serial="
8533 : " (SELECT merchant_serial"
8534 : " FROM merchant_instances"
8535 : " WHERE merchant_id=$7)",
8536 : 7),
8537 : /* for postgres_delete_transfer() */
8538 0 : GNUNET_PQ_make_prepare ("delete_transfer",
8539 : "DELETE FROM merchant_transfers"
8540 : " WHERE"
8541 : " credit_serial=$2"
8542 : " AND account_serial IN "
8543 : " (SELECT account_serial "
8544 : " FROM merchant_accounts"
8545 : " WHERE merchant_serial="
8546 : " (SELECT merchant_serial"
8547 : " FROM merchant_instances"
8548 : " WHERE merchant_id=$1))",
8549 : 2),
8550 : /* for postgres_check_transfer_exists() */
8551 0 : GNUNET_PQ_make_prepare ("check_transfer_exists",
8552 : "SELECT"
8553 : " 1"
8554 : " FROM merchant_transfers"
8555 : " JOIN merchant_accounts"
8556 : " USING (account_serial)"
8557 : " WHERE"
8558 : " credit_serial=$2"
8559 : " AND"
8560 : " merchant_serial="
8561 : " (SELECT merchant_serial"
8562 : " FROM merchant_instances"
8563 : " WHERE merchant_id=$1)",
8564 : 2),
8565 : /* for postgres_lookup_account() */
8566 0 : GNUNET_PQ_make_prepare ("lookup_account",
8567 : "SELECT"
8568 : " account_serial"
8569 : " FROM merchant_accounts"
8570 : " WHERE payto_uri=$2"
8571 : " AND merchant_serial="
8572 : " (SELECT merchant_serial"
8573 : " FROM merchant_instances"
8574 : " WHERE merchant_id=$1)",
8575 : 2),
8576 : /* for postgres_insert_transfer_details() */
8577 0 : GNUNET_PQ_make_prepare ("lookup_credit_serial",
8578 : "SELECT"
8579 : " credit_serial"
8580 : " FROM merchant_transfers"
8581 : " WHERE exchange_url=$1"
8582 : " AND wtid=$4"
8583 : " AND account_serial="
8584 : " (SELECT account_serial"
8585 : " FROM merchant_accounts"
8586 : " WHERE payto_uri=$2"
8587 : " AND exchange_url=$1"
8588 : " AND merchant_serial="
8589 : " (SELECT merchant_serial"
8590 : " FROM merchant_instances"
8591 : " WHERE merchant_id=$3))",
8592 : 4),
8593 : /* for postgres_insert_transfer_details() */
8594 0 : GNUNET_PQ_make_prepare ("insert_transfer_signature",
8595 : "INSERT INTO merchant_transfer_signatures"
8596 : "(credit_serial"
8597 : ",signkey_serial"
8598 : ",credit_amount_val"
8599 : ",credit_amount_frac"
8600 : ",wire_fee_val"
8601 : ",wire_fee_frac"
8602 : ",execution_time"
8603 : ",exchange_sig) "
8604 : "SELECT $1, signkey_serial, $2, $3, $4, $5, $6, $7"
8605 : " FROM merchant_exchange_signing_keys"
8606 : " WHERE exchange_pub=$8"
8607 : " ORDER BY start_date DESC"
8608 : " LIMIT 1",
8609 : 8),
8610 : /* for postgres_insert_transfer_details() */
8611 0 : GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping",
8612 : "INSERT INTO merchant_transfer_to_coin"
8613 : "(deposit_serial"
8614 : ",credit_serial"
8615 : ",offset_in_exchange_list"
8616 : ",exchange_deposit_value_val"
8617 : ",exchange_deposit_value_frac"
8618 : ",exchange_deposit_fee_val"
8619 : ",exchange_deposit_fee_frac) "
8620 : "SELECT deposit_serial, $1, $2, $3, $4, $5, $6"
8621 : " FROM merchant_deposits"
8622 : " JOIN merchant_contract_terms USING (order_serial)"
8623 : " WHERE coin_pub=$7"
8624 : " AND h_contract_terms=$8"
8625 : " AND merchant_serial="
8626 : " (SELECT merchant_serial"
8627 : " FROM merchant_instances"
8628 : " WHERE merchant_id=$9)",
8629 : 9),
8630 : /* for postgres_insert_transfer_details() */
8631 0 : GNUNET_PQ_make_prepare ("update_wired_by_coin_pub",
8632 : "WITH os AS" /* select orders affected by the coin */
8633 : "(SELECT order_serial"
8634 : " FROM merchant_deposits"
8635 : " WHERE coin_pub=$1)"
8636 : "UPDATE merchant_contract_terms "
8637 : " SET wired=TRUE "
8638 : " WHERE order_serial IN "
8639 : " (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/
8640 : " WHERE NOT EXISTS "
8641 : " (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */
8642 : " JOIN os USING (order_serial)" /* filter early */
8643 : " WHERE deposit_serial NOT IN"
8644 : " (SELECT deposit_serial " /* all coins associated with order that WERE wired */
8645 : " FROM merchant_deposits "
8646 : " JOIN os USING (order_serial)" /* filter early */
8647 : " JOIN merchant_deposit_to_transfer USING (deposit_serial)"
8648 : " JOIN merchant_transfers USING (credit_serial)"
8649 : " WHERE confirmed=TRUE)))",
8650 : 1),
8651 : /* for postgres_lookup_wire_fee() */
8652 0 : GNUNET_PQ_make_prepare ("lookup_wire_fee",
8653 : "SELECT"
8654 : " wire_fee_val"
8655 : ",wire_fee_frac"
8656 : ",closing_fee_val"
8657 : ",closing_fee_frac"
8658 : ",wad_fee_val"
8659 : ",wad_fee_frac"
8660 : ",start_date"
8661 : ",end_date"
8662 : ",master_sig"
8663 : " FROM merchant_exchange_wire_fees"
8664 : " WHERE master_pub=$1"
8665 : " AND h_wire_method=$2"
8666 : " AND start_date <= $3"
8667 : " AND end_date > $3",
8668 : 3),
8669 : /* for postgres_lookup_deposits_by_contract_and_coin() */
8670 0 : GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin",
8671 : "SELECT"
8672 : " exchange_url"
8673 : ",amount_with_fee_val"
8674 : ",amount_with_fee_frac"
8675 : ",deposit_fee_val"
8676 : ",deposit_fee_frac"
8677 : ",refund_fee_val"
8678 : ",refund_fee_frac"
8679 : ",wire_fee_val"
8680 : ",wire_fee_frac"
8681 : ",h_wire"
8682 : ",deposit_timestamp"
8683 : ",refund_deadline"
8684 : ",exchange_sig"
8685 : ",exchange_pub"
8686 : " FROM merchant_contract_terms"
8687 : " JOIN merchant_deposits USING (order_serial)"
8688 : " JOIN merchant_exchange_signing_keys USING (signkey_serial)"
8689 : " JOIN merchant_accounts USING (account_serial)"
8690 : " WHERE h_contract_terms=$2"
8691 : " AND coin_pub=$3"
8692 : " AND merchant_contract_terms.merchant_serial="
8693 : " (SELECT merchant_serial"
8694 : " FROM merchant_instances"
8695 : " WHERE merchant_id=$1)",
8696 : 3),
8697 : /* for postgres_lookup_transfer() */
8698 0 : GNUNET_PQ_make_prepare ("lookup_transfer",
8699 : "SELECT"
8700 : " mt.credit_amount_val AS credit_amount_val"
8701 : ",mt.credit_amount_frac AS credit_amount_frac"
8702 : ",mts.credit_amount_val AS exchange_amount_val"
8703 : ",mts.credit_amount_frac AS exchange_amount_frac"
8704 : ",wire_fee_val"
8705 : ",wire_fee_frac"
8706 : ",execution_time"
8707 : ",verified"
8708 : " FROM merchant_transfers mt"
8709 : " JOIN merchant_accounts USING (account_serial)"
8710 : " JOIN merchant_instances USING (merchant_serial)"
8711 : " LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)"
8712 : " WHERE wtid=$2"
8713 : " AND exchange_url=$1"
8714 : " AND merchant_id=$3;",
8715 : 3),
8716 : /* for postgres_set_transfer_status_to_verified() */
8717 0 : GNUNET_PQ_make_prepare ("set_transfer_status_to_verified",
8718 : "UPDATE merchant_transfers SET"
8719 : " verified=TRUE"
8720 : " WHERE wtid=$1"
8721 : " AND exchange_url=$2",
8722 : 2),
8723 : /* for postgres_lookup_transfer_summary() */
8724 0 : GNUNET_PQ_make_prepare ("lookup_transfer_summary",
8725 : "SELECT"
8726 : " order_id"
8727 : ",exchange_deposit_value_val"
8728 : ",exchange_deposit_value_frac"
8729 : ",exchange_deposit_fee_val"
8730 : ",exchange_deposit_fee_frac"
8731 : " FROM merchant_transfers"
8732 : " JOIN merchant_transfer_to_coin USING (credit_serial)"
8733 : " JOIN merchant_deposits USING (deposit_serial)"
8734 : " JOIN merchant_contract_terms USING (order_serial)"
8735 : " WHERE wtid=$2"
8736 : " AND merchant_transfers.exchange_url=$1",
8737 : 2),
8738 : /* for postgres_lookup_transfer_details() */
8739 0 : GNUNET_PQ_make_prepare ("lookup_transfer_details",
8740 : "SELECT"
8741 : " merchant_contract_terms.h_contract_terms"
8742 : ",merchant_transfer_to_coin.offset_in_exchange_list"
8743 : ",merchant_deposits.coin_pub"
8744 : ",exchange_deposit_value_val"
8745 : ",exchange_deposit_value_frac"
8746 : ",exchange_deposit_fee_val"
8747 : ",exchange_deposit_fee_frac"
8748 : " FROM merchant_transfer_to_coin"
8749 : " JOIN merchant_deposits USING (deposit_serial)"
8750 : " JOIN merchant_contract_terms USING (order_serial)"
8751 : " JOIN merchant_transfers USING (credit_serial)"
8752 : " WHERE merchant_transfers.wtid=$2"
8753 : " AND merchant_transfers.exchange_url=$1",
8754 : 2),
8755 : /* for postgres_lookup_transfers() */
8756 0 : GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_asc",
8757 : "SELECT"
8758 : " mt.credit_amount_val"
8759 : ",mt.credit_amount_frac"
8760 : ",wtid"
8761 : ",merchant_accounts.payto_uri"
8762 : ",exchange_url"
8763 : ",credit_serial"
8764 : ",merchant_transfer_signatures.execution_time"
8765 : ",verified"
8766 : ",confirmed"
8767 : " FROM merchant_transfers mt"
8768 : " JOIN merchant_accounts USING (account_serial)"
8769 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8770 : " WHERE execution_time < $2"
8771 : " AND execution_time >= $3"
8772 : " AND credit_serial > $4"
8773 : " AND payto_uri = $6"
8774 : " AND merchant_serial ="
8775 : " (SELECT merchant_serial"
8776 : " FROM merchant_instances"
8777 : " WHERE merchant_id=$1)"
8778 : " ORDER BY credit_serial ASC"
8779 : " LIMIT $5",
8780 : 6),
8781 : /* for postgres_lookup_transfers() */
8782 0 : GNUNET_PQ_make_prepare ("lookup_transfers_time_asc",
8783 : "SELECT"
8784 : " mt.credit_amount_val"
8785 : ",mt.credit_amount_frac"
8786 : ",wtid"
8787 : ",merchant_accounts.payto_uri"
8788 : ",exchange_url"
8789 : ",credit_serial"
8790 : ",merchant_transfer_signatures.execution_time"
8791 : ",verified"
8792 : ",confirmed"
8793 : " FROM merchant_transfers mt"
8794 : " JOIN merchant_accounts USING (account_serial)"
8795 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8796 : " WHERE execution_time < $2"
8797 : " AND execution_time >= $3"
8798 : " AND credit_serial > $4"
8799 : " AND merchant_serial ="
8800 : " (SELECT merchant_serial"
8801 : " FROM merchant_instances"
8802 : " WHERE merchant_id=$1)"
8803 : " ORDER BY credit_serial ASC"
8804 : " LIMIT $5",
8805 : 5),
8806 : /* for postgres_lookup_transfers() */
8807 0 : GNUNET_PQ_make_prepare ("lookup_transfers_payto_asc",
8808 : "SELECT"
8809 : " mt.credit_amount_val"
8810 : ",mt.credit_amount_frac"
8811 : ",wtid"
8812 : ",merchant_accounts.payto_uri"
8813 : ",exchange_url"
8814 : ",credit_serial"
8815 : ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
8816 : " THEN 9223372036854775807" /* largest BIGINT possible */
8817 : " ELSE merchant_transfer_signatures.execution_time"
8818 : " END AS execution_time"
8819 : ",verified"
8820 : ",confirmed"
8821 : " FROM merchant_transfers mt"
8822 : " JOIN merchant_accounts USING (account_serial)"
8823 : " LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
8824 : " WHERE credit_serial > $2"
8825 : " AND payto_uri = $4"
8826 : " AND merchant_serial ="
8827 : " (SELECT merchant_serial"
8828 : " FROM merchant_instances"
8829 : " WHERE merchant_id=$1)"
8830 : " ORDER BY credit_serial ASC"
8831 : " LIMIT $3",
8832 : 4),
8833 : /* for postgres_lookup_transfers() */
8834 0 : GNUNET_PQ_make_prepare ("lookup_transfers_asc",
8835 : "SELECT"
8836 : " mt.credit_amount_val"
8837 : ",mt.credit_amount_frac"
8838 : ",wtid"
8839 : ",merchant_accounts.payto_uri"
8840 : ",exchange_url"
8841 : ",credit_serial"
8842 : ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
8843 : " THEN 9223372036854775807" /* largest BIGINT possible */
8844 : " ELSE merchant_transfer_signatures.execution_time"
8845 : " END AS execution_time"
8846 : ",verified"
8847 : ",confirmed"
8848 : " FROM merchant_transfers mt"
8849 : " JOIN merchant_accounts USING (account_serial)"
8850 : " LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
8851 : " WHERE credit_serial > $2"
8852 : " AND merchant_serial ="
8853 : " (SELECT merchant_serial"
8854 : " FROM merchant_instances"
8855 : " WHERE merchant_id=$1)"
8856 : " ORDER BY credit_serial ASC"
8857 : " LIMIT $3",
8858 : 3),
8859 : /* for postgres_lookup_transfers() */
8860 0 : GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_desc",
8861 : "SELECT"
8862 : " mt.credit_amount_val"
8863 : ",mt.credit_amount_frac"
8864 : ",wtid"
8865 : ",merchant_accounts.payto_uri"
8866 : ",exchange_url"
8867 : ",credit_serial"
8868 : ",merchant_transfer_signatures.execution_time"
8869 : ",verified"
8870 : ",confirmed"
8871 : " FROM merchant_transfers mt"
8872 : " JOIN merchant_accounts USING (account_serial)"
8873 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8874 : " WHERE execution_time < $2"
8875 : " AND execution_time >= $3"
8876 : " AND credit_serial < $4"
8877 : " AND payto_uri = $6"
8878 : " AND merchant_serial ="
8879 : " (SELECT merchant_serial"
8880 : " FROM merchant_instances"
8881 : " WHERE merchant_id=$1)"
8882 : " ORDER BY credit_serial DESC"
8883 : " LIMIT $5",
8884 : 6),
8885 : /* for postgres_lookup_transfers() */
8886 0 : GNUNET_PQ_make_prepare ("lookup_transfers_time_desc",
8887 : "SELECT"
8888 : " mt.credit_amount_val"
8889 : ",mt.credit_amount_frac"
8890 : ",wtid"
8891 : ",merchant_accounts.payto_uri"
8892 : ",exchange_url"
8893 : ",credit_serial& |