/* This file is part of TALER (C) 2014--2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file plugin_merchantdb_postgres.c * @brief database helper functions for postgres used by the merchant * @author Sree Harsha Totakura * @author Christian Grothoff * @author Marcello Stanisci * @author Priscilla Huang * @author Iván Ávalos */ #include "platform.h" #include #include #include #include #include #include #include "taler_merchantdb_plugin.h" #include "pg_helper.h" #include "pg_insert_account.h" #include "pg_update_account.h" #include "pg_lookup_instances.h" #include "pg_lookup_transfers.h" #include "pg_update_wirewatch_progress.h" #include "pg_select_wirewatch_accounts.h" #include "pg_select_open_transfers.h" #include "pg_delete_exchange_accounts.h" #include "pg_select_accounts_by_exchange.h" #include "pg_insert_exchange_account.h" #include "pg_lookup_reserves.h" #include "pg_lookup_instance_auth.h" #include "pg_update_transfer_status.h" #include "pg_insert_instance.h" #include "pg_account_kyc_set_status.h" #include "pg_account_kyc_get_status.h" #include "pg_delete_instance_private_key.h" #include "pg_purge_instance.h" #include "pg_update_instance.h" #include "pg_update_instance_auth.h" #include "pg_inactivate_account.h" #include "pg_activate_account.h" #include "pg_lookup_products.h" #include "pg_lookup_product.h" #include "pg_delete_product.h" #include "pg_insert_product.h" #include "pg_update_product.h" #include "pg_lock_product.h" #include "pg_expire_locks.h" #include "pg_delete_order.h" #include "pg_lookup_order.h" #include "pg_lookup_order_summary.h" #include "pg_lookup_orders.h" #include "pg_insert_order.h" #include "pg_unlock_inventory.h" #include "pg_insert_order_lock.h" #include "pg_lookup_contract_terms2.h" #include "pg_lookup_contract_terms.h" #include "pg_insert_contract_terms.h" #include "pg_update_contract_terms.h" #include "pg_delete_contract_terms.h" #include "pg_lookup_deposits.h" #include "pg_insert_exchange_signkey.h" #include "pg_insert_deposit.h" #include "pg_lookup_refunds.h" #include "pg_mark_contract_paid.h" #include "pg_refund_coin.h" #include "pg_lookup_order_status.h" #include "pg_lookup_order_status_by_serial.h" #include "pg_lookup_payment_status.h" #include "pg_set_transfer_status_to_confirmed.h" #include "pg_insert_exchange_keys.h" #include "pg_select_exchange_keys.h" #include "pg_insert_deposit_to_transfer.h" #include "pg_increase_refund.h" #include "pg_insert_transfer.h" #include "pg_insert_transfer_details.h" #include "pg_store_wire_fee_by_exchange.h" #include "pg_insert_reserve.h" #include "pg_activate_reserve.h" #include "pg_authorize_reward.h" #include "pg_insert_pickup.h" /** * How often do we re-try if we run into a DB serialization error? */ #define MAX_RETRIES 3 /** * Drop all Taler tables. This should only be used by testcases. * * @param cls the `struct PostgresClosure` with the plugin-specific state * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ static enum GNUNET_GenericReturnValue postgres_drop_tables (void *cls) { struct PostgresClosure *pc = cls; struct GNUNET_PQ_Context *conn; enum GNUNET_GenericReturnValue ret; conn = GNUNET_PQ_connect_with_cfg (pc->cfg, "merchantdb-postgres", NULL, NULL, NULL); if (NULL == conn) return GNUNET_SYSERR; ret = GNUNET_PQ_exec_sql (conn, "drop"); GNUNET_PQ_disconnect (conn); return ret; } /** * Initialize tables. * * @param cls the `struct PostgresClosure` with the plugin-specific state * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ static enum GNUNET_GenericReturnValue postgres_create_tables (void *cls) { struct PostgresClosure *pc = cls; struct GNUNET_PQ_Context *conn; struct GNUNET_PQ_ExecuteStatement es[] = { GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"), GNUNET_PQ_EXECUTE_STATEMENT_END }; conn = GNUNET_PQ_connect_with_cfg (pc->cfg, "merchantdb-postgres", "merchant-", es, NULL); if (NULL == conn) return GNUNET_SYSERR; GNUNET_PQ_disconnect (conn); return GNUNET_OK; } /** * Register callback to be invoked on events of type @a es. * * @param cls database context to use * @param es specification of the event to listen for * @param timeout how long to wait for the event * @param cb function to call when the event happens, possibly * mulrewardle times (until cancel is invoked) * @param cb_cls closure for @a cb * @return handle useful to cancel the listener */ static struct GNUNET_DB_EventHandler * postgres_event_listen (void *cls, const struct GNUNET_DB_EventHeaderP *es, struct GNUNET_TIME_Relative timeout, GNUNET_DB_EventCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; return GNUNET_PQ_event_listen (pg->conn, es, timeout, cb, cb_cls); } /** * Stop notifications. * * @param eh handle to unregister. */ static void postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh) { GNUNET_PQ_event_listen_cancel (eh); } /** * Notify all that listen on @a es of an event. * * @param cls database context to use * @param es specification of the event to generate * @param extra additional event data provided * @param extra_size number of bytes in @a extra */ static void postgres_event_notify (void *cls, const struct GNUNET_DB_EventHeaderP *es, const void *extra, size_t extra_size) { struct PostgresClosure *pg = cls; return GNUNET_PQ_event_notify (pg->conn, es, extra, extra_size); } void postgres_preflight (void *cls) { struct PostgresClosure *pg = cls; if (NULL == pg->transaction_name) return; /* all good */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "BUG: Preflight check detected running transaction `%s'!\n", pg->transaction_name); GNUNET_assert (0); } void check_connection (struct PostgresClosure *pg) { if (NULL != pg->transaction_name) return; GNUNET_PQ_reconnect_if_down (pg->conn); } /** * Closure for lookup_deposits_by_order_cb(). */ struct LookupDepositsByOrderContext { /** * Plugin context. */ struct PostgresClosure *pg; /** * Function to call with all results. */ TALER_MERCHANTDB_DepositedCoinsCallback cb; /** * Closure for @e cb. */ void *cb_cls; /** * Set to the query result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * * @param cls of type `struct LookupDepositsByOrderContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_deposits_by_order_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupDepositsByOrderContext *ldoc = cls; struct PostgresClosure *pg = ldoc->pg; for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR; return; } ldoc->cb (ldoc->cb_cls, deposit_serial, exchange_url, &h_wire, &amount_with_fee, &deposit_fee, &coin_pub); GNUNET_PQ_cleanup_result (rs); /* technically useless here */ } ldoc->qs = num_results; } /** * Retrieve details about coins that were deposited for an order. * * @param cls closure * @param order_serial identifies the order * @param cb function to call for each deposited coin * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_deposits_by_order (void *cls, uint64_t order_serial, TALER_MERCHANTDB_DepositedCoinsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupDepositsByOrderContext ldoc = { .pg = pg, .cb = cb, .cb_cls = cb_cls }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&order_serial), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_deposits_by_order", params, &lookup_deposits_by_order_cb, &ldoc); if (qs < 0) return qs; return ldoc.qs; } /** * Closure for lookup_deposits_by_order_cb(). */ struct LookupTransferDetailsByOrderContext { /** * Plugin context. */ struct PostgresClosure *pg; /** * Function to call with all results. */ TALER_MERCHANTDB_OrderTransferDetailsCallback cb; /** * Closure for @e cb. */ void *cb_cls; /** * Set to the query result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * * @param cls of type `struct LookupTransferDetailsByOrderContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_transfer_details_by_order_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupTransferDetailsByOrderContext *ltdo = cls; struct PostgresClosure *pg = ltdo->pg; for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR; return; } ltdo->cb (ltdo->cb_cls, &wtid, exchange_url, execution_time, &deposit_value, &deposit_fee, (0 != transfer_confirmed)); GNUNET_PQ_cleanup_result (rs); /* technically useless here */ } ltdo->qs = num_results; } /** * Retrieve wire transfer details for all deposits associated with * a given @a order_serial. * * @param cls closure * @param order_serial identifies the order * @param cb function called with the wire transfer details * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_transfer_details_by_order ( void *cls, uint64_t order_serial, TALER_MERCHANTDB_OrderTransferDetailsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupTransferDetailsByOrderContext ltdo = { .pg = pg, .cb = cb, .cb_cls = cb_cls }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&order_serial), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "lookup_transfer_details_by_order", params, &lookup_transfer_details_by_order_cb, <do); if (qs < 0) return qs; return ltdo.qs; } /** * Set 'wired' status for an order to 'true'. * * @param cls closure * @param order_serial serial number of the order * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_mark_order_wired (void *cls, uint64_t order_serial) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&order_serial), GNUNET_PQ_query_param_end }; return GNUNET_PQ_eval_prepared_non_select (pg->conn, "mark_order_wired", params); } /** * Closure for #lookup_refunds_detailed_cb(). */ struct LookupRefundsDetailedContext { /** * Function to call for each refund. */ TALER_MERCHANTDB_RefundDetailCallback rc; /** * Closure for @e rc. */ void *rc_cls; /** * Plugin context. */ struct PostgresClosure *pg; /** * Transaction result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * * @param cls of type `struct GetRefundsContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_refunds_detailed_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupRefundsDetailedContext *lrdc = cls; struct PostgresClosure *pg = lrdc->pg; for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR; return; } lrdc->rc (lrdc->rc_cls, refund_serial, timestamp, &coin_pub, exchange_url, rtransaction_id, reason, &refund_amount, 0 != pending8); GNUNET_PQ_cleanup_result (rs); } lrdc->qs = num_results; } /** * Obtain detailed refund data associated with a contract. * * @param cls closure, typically a connection to the db * @param instance_id instance to lookup refunds for * @param h_contract_terms hash code of the contract * @param rc function to call for each coin on which there is a refund * @param rc_cls closure for @a rc * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_refunds_detailed ( void *cls, const char *instance_id, const struct TALER_PrivateContractHashP *h_contract_terms, TALER_MERCHANTDB_RefundDetailCallback rc, void *rc_cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_end }; struct LookupRefundsDetailedContext lrdc = { .rc = rc, .rc_cls = rc_cls, .pg = pg }; enum GNUNET_DB_QueryStatus qs; /* no preflight check here, run in transaction by caller! */ TALER_LOG_DEBUG ("Looking for refund %s + %s\n", GNUNET_h2s (&h_contract_terms->hash), instance_id); check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_refunds_detailed", params, &lookup_refunds_detailed_cb, &lrdc); if (0 >= qs) return qs; return lrdc.qs; } /** * Insert refund proof data from the exchange into the database. * * @param cls closure * @param refund_serial serial number of the refund * @param exchange_sig signature from exchange that coin was refunded * @param exchange_pub signing key that was used for @a exchange_sig * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_insert_refund_proof ( void *cls, uint64_t refund_serial, const struct TALER_ExchangeSignatureP *exchange_sig, const struct TALER_ExchangePublicKeyP *exchange_pub) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&refund_serial), GNUNET_PQ_query_param_auto_from_type (exchange_sig), GNUNET_PQ_query_param_auto_from_type (exchange_pub), GNUNET_PQ_query_param_end }; return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_refund_proof", params); } /** * Lookup refund proof data. * * @param cls closure * @param refund_serial serial number of the refund * @param[out] exchange_sig set to signature from exchange * @param[out] exchange_pub signing key that was used for @a exchange_sig * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_refund_proof (void *cls, uint64_t refund_serial, struct TALER_ExchangeSignatureP *exchange_sig, struct TALER_ExchangePublicKeyP *exchange_pub) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&refund_serial), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("exchange_sig", exchange_sig), GNUNET_PQ_result_spec_auto_from_type ("exchange_pub", exchange_pub), GNUNET_PQ_result_spec_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_refund_proof", params, rs); } /** * Retrieve the order ID that was used to pay for a resource within a session. * * @param cls closure * @param instance_id identifying the instance * @param fulfillment_url URL that canonically identifies the resource * being paid for * @param session_id session id * @param[out] order_id where to store the order ID that was used when * paying for the resource URL * @return transaction status */ enum GNUNET_DB_QueryStatus postgres_lookup_order_by_fulfillment (void *cls, const char *instance_id, const char *fulfillment_url, const char *session_id, char **order_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (fulfillment_url), GNUNET_PQ_query_param_string (session_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("order_id", order_id), GNUNET_PQ_result_spec_end }; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_order_by_fulfillment", params, rs); } /** * Delete information about a transfer. Note that transfers * confirmed by the exchange cannot be deleted anymore. * * @param cls closure * @param instance_id instance to delete transfer of * @param transfer_serial_id transfer to delete * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS * if deletion is prohibited OR transfer is unknown */ static enum GNUNET_DB_QueryStatus postgres_delete_transfer (void *cls, const char *instance_id, uint64_t transfer_serial_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&transfer_serial_id), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "delete_transfer", params); } /** * Check if information about a transfer exists with the * backend. Returns no data, only the query status. * * @param cls closure * @param instance_id instance to delete transfer of * @param transfer_serial_id transfer to delete * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT * if the transfer record exists */ static enum GNUNET_DB_QueryStatus postgres_check_transfer_exists (void *cls, const char *instance_id, uint64_t transfer_serial_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&transfer_serial_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "check_transfer_exists", params, rs); } /** * Lookup account serial by payto URI. * * @param cls closure * @param instance_id instance to lookup the account from * @param payto_uri what is the merchant's bank account to lookup * @param[out] account_serial serial number of the account * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_account (void *cls, const char *instance_id, const char *payto_uri, uint64_t *account_serial) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (payto_uri), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("account_serial", account_serial), GNUNET_PQ_result_spec_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_account", params, rs); } /** * Obtain information about wire fees charged by an exchange, * including signature (so we have proof). * * @param cls closure * @param master_pub public key of the exchange * @param wire_method the wire method * @param contract_date date of the contract to use for the lookup * @param[out] fees wire fees charged * @param[out] start_date start of fee being used * @param[out] end_date end of fee being used * @param[out] master_sig signature of exchange over fee structure * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_lookup_wire_fee (void *cls, const struct TALER_MasterPublicKeyP *master_pub, const char *wire_method, struct GNUNET_TIME_Timestamp contract_date, struct TALER_WireFeeSet *fees, struct GNUNET_TIME_Timestamp *start_date, struct GNUNET_TIME_Timestamp *end_date, struct TALER_MasterSignatureP *master_sig) { struct PostgresClosure *pg = cls; struct GNUNET_HashCode h_wire_method; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_auto_from_type (&h_wire_method), GNUNET_PQ_query_param_timestamp (&contract_date), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", &fees->wire), TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee", &fees->closing), GNUNET_PQ_result_spec_timestamp ("start_date", start_date), GNUNET_PQ_result_spec_timestamp ("end_date", end_date), GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig), GNUNET_PQ_result_spec_end }; check_connection (pg); GNUNET_CRYPTO_hash (wire_method, strlen (wire_method) + 1, &h_wire_method); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_wire_fee", params, rs); } /** * Closure for #lookup_deposits_by_contract_and_coin_cb(). */ struct LookupDepositsByCnCContext { /** * Function to call for each deposit. */ TALER_MERCHANTDB_CoinDepositCallback cb; /** * Closure for @e cb. */ void *cb_cls; /** * Plugin context. */ struct PostgresClosure *pg; /** * Transaction result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * * @param cls of type `struct LookupDepositsByCnCContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_deposits_by_contract_and_coin_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupDepositsByCnCContext *ldcc = cls; struct PostgresClosure *pg = ldcc->pg; for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR; return; } ldcc->cb (ldcc->cb_cls, exchange_url, &amount_with_fee, &deposit_fee, &refund_fee, &wire_fee, &h_wire, deposit_timestamp, refund_deadline, &exchange_sig, &exchange_pub); GNUNET_PQ_cleanup_result (rs); } ldcc->qs = num_results; } /** * Lookup information about coin payments by @a h_contract_terms and * @a coin_pub. * * @param cls closure * @param instance_id instance to lookup payments for * @param h_contract_terms proposal data's hashcode * @param coin_pub public key to use for the search * @param cb function to call with payment data * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_deposits_by_contract_and_coin ( void *cls, const char *instance_id, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, TALER_MERCHANTDB_CoinDepositCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (coin_pub), GNUNET_PQ_query_param_end }; struct LookupDepositsByCnCContext ldcc = { .cb = cb, .cb_cls = cb_cls, .pg = pg }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "lookup_deposits_by_contract_and_coin", params, &lookup_deposits_by_contract_and_coin_cb, &ldcc); if (0 >= qs) return qs; return ldcc.qs; } /** * Lookup transfer status. * * @param cls closure * @param instance_id at which instance should we resolve the transfer * @param exchange_url the exchange that made the transfer * @param wtid wire transfer subject * @param[out] total_amount amount that was debited from our * aggregate balance at the exchange (in total, sum of * the wire transfer amount and the @a wire_fee) * @param[out] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true) * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true) * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true) * @param[out] have_exchange_sig do we have a response from the exchange about this transfer * @param[out] verified did we confirm the transfer was OK * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_transfer ( void *cls, const char *instance_id, const char *exchange_url, const struct TALER_WireTransferIdentifierRawP *wtid, struct TALER_Amount *total_amount, struct TALER_Amount *wire_fee, struct TALER_Amount *exchange_amount, struct GNUNET_TIME_Timestamp *execution_time, bool *have_exchange_sig, bool *verified) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_auto_from_type (wtid), GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_end }; uint8_t verified8; /** Amount we got actually credited, _excludes_ the wire fee */ bool no_sig; struct TALER_Amount credit_amount; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount", &credit_amount), GNUNET_PQ_result_spec_allow_null ( TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee", wire_fee), &no_sig), GNUNET_PQ_result_spec_allow_null ( TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount", exchange_amount), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_timestamp ("execution_time", execution_time), NULL), GNUNET_PQ_result_spec_auto_from_type ("verified", &verified8), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); *execution_time = GNUNET_TIME_UNIT_ZERO_TS; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_transfer", params, rs); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup transfer returned %d\n", qs); if (qs > 0) { *have_exchange_sig = ! no_sig; *verified = (0 != verified8); if ( (! no_sig) && (0 > TALER_amount_add (total_amount, &credit_amount, wire_fee)) ) { GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } } else { *verified = false; *have_exchange_sig = false; } return qs; } /** * Closure for #lookup_transfer_summary_cb(). */ struct LookupTransferSummaryContext { /** * Function to call for each order that was aggregated. */ TALER_MERCHANTDB_TransferSummaryCallback cb; /** * Closure for @e cb. */ void *cb_cls; /** * Plugin context. */ struct PostgresClosure *pg; /** * Transaction result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * * @param cls of type `struct LookupTransferSummaryContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_transfer_summary_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupTransferSummaryContext *ltdc = cls; struct PostgresClosure *pg = ltdc->pg; for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR; return; } ltdc->cb (ltdc->cb_cls, order_id, &deposit_value, &deposit_fee); GNUNET_PQ_cleanup_result (rs); } ltdc->qs = num_results; } /** * Lookup transfer summary. * * @param cls closure * @param exchange_url the exchange that made the transfer * @param wtid wire transfer subject * @param cb function to call with detailed transfer data * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_transfer_summary ( void *cls, const char *exchange_url, const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MERCHANTDB_TransferSummaryCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_auto_from_type (wtid), GNUNET_PQ_query_param_end }; struct LookupTransferSummaryContext ltdc = { .cb = cb, .cb_cls = cb_cls, .pg = pg }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "lookup_transfer_summary", params, &lookup_transfer_summary_cb, <dc); if (0 >= qs) return qs; return ltdc.qs; } /** * Closure for #lookup_transfer_details_cb(). */ struct LookupTransferDetailsContext { /** * Function to call for each order that was aggregated. */ TALER_MERCHANTDB_TransferDetailsCallback cb; /** * Closure for @e cb. */ void *cb_cls; /** * Plugin context. */ struct PostgresClosure *pg; /** * Transaction result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * * @param cls of type `struct LookupTransferDetailsContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_transfer_details_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupTransferDetailsContext *ltdc = cls; struct PostgresClosure *pg = ltdc->pg; for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR; return; } ltdc->cb (ltdc->cb_cls, (unsigned int) current_offset, &ttd); GNUNET_PQ_cleanup_result (rs); } ltdc->qs = num_results; } /** * Lookup transfer details. * * @param cls closure * @param exchange_url the exchange that made the transfer * @param wtid wire transfer subject * @param cb function to call with detailed transfer data * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_transfer_details ( void *cls, const char *exchange_url, const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MERCHANTDB_TransferDetailsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_auto_from_type (wtid), GNUNET_PQ_query_param_end }; struct LookupTransferDetailsContext ltdc = { .cb = cb, .cb_cls = cb_cls, .pg = pg }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "lookup_transfer_details", params, &lookup_transfer_details_cb, <dc); if (0 >= qs) return qs; return ltdc.qs; } /** * Closure for #lookup_pending_reserves_cb. */ struct LookupPendingReservesContext { /** * Postgres context. */ struct PostgresClosure *pg; /** * Function to call with the results */ TALER_MERCHANTDB_PendingReservesCallback cb; /** * Closure for @e cb */ void *cb_cls; /** * Set in case of errors. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about accounts. * * @param[in,out] cls of type `struct LookupReservesContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_pending_reserves_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupPendingReservesContext *lrc = cls; struct PostgresClosure *pg = lrc->pg; for (unsigned int i = 0; i < num_results; i++) { struct TALER_ReservePublicKeyP reserve_pub; struct TALER_Amount merchant_initial_balance; char *exchange_url; char *instance_id; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", &reserve_pub), GNUNET_PQ_result_spec_string ("merchant_id", &instance_id), GNUNET_PQ_result_spec_string ("exchange_url", &exchange_url), TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance", &merchant_initial_balance), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); lrc->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } lrc->cb (lrc->cb_cls, instance_id, exchange_url, &reserve_pub, &merchant_initial_balance); GNUNET_PQ_cleanup_result (rs); } } /** * Lookup reserves pending activation across all instances. * * @param cls closure * @param cb function to call with reserve summary data * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_pending_reserves (void *cls, TALER_MERCHANTDB_PendingReservesCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupPendingReservesContext lrc = { .pg = pg, .cb = cb, .cb_cls = cb_cls }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_pending_reserves", params, &lookup_pending_reserves_cb, &lrc); if (lrc.qs < 0) return lrc.qs; return qs; } /** * Closure for #lookup_reserve_rewards_cb(). */ struct LookupRewardsContext { /** * Postgres context. */ struct PostgresClosure *pg; /** * Array with information about rewards generated from this reserve. */ struct TALER_MERCHANTDB_RewardDetails *rewards; /** * Length of the @e rewards array. */ unsigned int rewards_length; /** * Set in case of errors. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about accounts. * * @param[in,out] cls of type `struct LookupRewardsContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_reserve_rewards_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupRewardsContext *ltc = cls; struct PostgresClosure *pg = ltc->pg; GNUNET_array_grow (ltc->rewards, ltc->rewards_length, num_results); for (unsigned int i = 0; i < num_results; i++) { struct TALER_MERCHANTDB_RewardDetails *td = <c->rewards[i]; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("justification", &td->reason), GNUNET_PQ_result_spec_auto_from_type ("reward_id", &td->reward_id), TALER_PQ_RESULT_SPEC_AMOUNT ("amount", &td->total_amount), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); ltc->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } } } /** * Lookup reserve details. * * @param cls closure * @param instance_id instance to lookup payments for * @param reserve_pub public key of the reserve to inspect * @param fetch_rewards if true, also return information about rewards * @param cb function to call with reserve summary data * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_reserve (void *cls, const char *instance_id, const struct TALER_ReservePublicKeyP *reserve_pub, bool fetch_rewards, TALER_MERCHANTDB_ReserveDetailsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupRewardsContext ltc = { .pg = pg, .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_end }; struct GNUNET_TIME_Timestamp creation_time; struct GNUNET_TIME_Timestamp expiration_time; struct TALER_Amount merchant_initial_balance; struct TALER_Amount exchange_initial_balance; struct TALER_Amount pickup_amount; struct TALER_Amount committed_amount; struct TALER_MasterPublicKeyP master_pub; bool active; char *exchange_url = NULL; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_timestamp ("creation_time", &creation_time), GNUNET_PQ_result_spec_timestamp ("expiration", &expiration_time), TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance", &merchant_initial_balance), TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance", &exchange_initial_balance), TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_picked_up", &pickup_amount), TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_committed", &committed_amount), GNUNET_PQ_result_spec_auto_from_type ("master_pub", &master_pub), GNUNET_PQ_result_spec_bool ("active", &active), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("exchange_url", &exchange_url), NULL), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_reserve", params, rs); if (qs < 0) return qs; if (! fetch_rewards) { cb (cb_cls, creation_time, expiration_time, &merchant_initial_balance, &exchange_initial_balance, &pickup_amount, &committed_amount, active, &master_pub, exchange_url, 0, NULL); GNUNET_PQ_cleanup_result (rs); return qs; } qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_reserve_rewards", params, &lookup_reserve_rewards_cb, <c); if (qs < 0) return qs; if (ltc.qs >= 0) { cb (cb_cls, creation_time, expiration_time, &merchant_initial_balance, &exchange_initial_balance, &pickup_amount, &committed_amount, active, &master_pub, exchange_url, ltc.rewards_length, ltc.rewards); } for (unsigned int i = 0; iconn, "delete_reserve", params); } /** * Purge all of the information about a reserve, including rewards. * * @param cls closure, typically a connection to the db * @param instance_id which instance is the reserve tied to * @param reserve_pub which reserve is to be purged * @return transaction status, usually * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success */ static enum GNUNET_DB_QueryStatus postgres_purge_reserve (void *cls, const char *instance_id, const struct TALER_ReservePublicKeyP *reserve_pub) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "purge_reserve", params); } /** * Closure for #lookup_signatures_cb(). */ struct LookupSignaturesContext { /** * Length of the @e sigs array */ unsigned int sigs_length; /** * Where to store the signatures. */ struct TALER_BlindedDenominationSignature *sigs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about accounts. * * @param[in,out] cls of type `struct LookupSignaturesContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_signatures_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupSignaturesContext *lsc = cls; for (unsigned int i = 0; i < num_results; i++) { uint32_t offset; struct TALER_BlindedDenominationSignature bsig; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint32 ("coin_offset", &offset), TALER_PQ_result_spec_blinded_denom_sig ("blind_sig", &bsig), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); return; } if (offset >= lsc->sigs_length) { GNUNET_break_op (0); GNUNET_PQ_cleanup_result (rs); continue; } /* Must be NULL due to UNIQUE constraint on offset and requirement that client launched us with 'sigs' pre-initialized to NULL. */ lsc->sigs[offset] = bsig; } } /** * Lookup pickup details for pickup @a pickup_id. * * @param cls closure, typically a connection to the db * @param instance_id which instance should we lookup reward details for * @param reward_id which reward should we lookup details on * @param pickup_id which pickup should we lookup details on * @param[out] exchange_url which exchange is the reward withdrawn from * @param[out] reserve_priv private key the reward is withdrawn from (set if still available!) * @param sigs_length length of the @a sigs array * @param[out] sigs set to the (blind) signatures we have for this @a pickup_id, * those that are unavailable are left at NULL * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_pickup (void *cls, const char *instance_id, const struct TALER_RewardIdentifierP *reward_id, const struct TALER_PickupIdentifierP *pickup_id, char **exchange_url, struct TALER_ReservePrivateKeyP *reserve_priv, unsigned int sigs_length, struct TALER_BlindedDenominationSignature sigs[]) { struct PostgresClosure *pg = cls; uint64_t pickup_serial; { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (reward_id), GNUNET_PQ_query_param_auto_from_type (pickup_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("exchange_url", exchange_url), GNUNET_PQ_result_spec_auto_from_type ("reserve_priv", reserve_priv), GNUNET_PQ_result_spec_uint64 ("pickup_serial", &pickup_serial), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_pickup", params, rs); if (qs <= 0) return qs; } { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&pickup_serial), GNUNET_PQ_query_param_end }; struct LookupSignaturesContext lsc = { .sigs_length = sigs_length, .sigs = sigs }; return GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_pickup_signatures", params, &lookup_signatures_cb, &lsc); } } /** * Lookup reward details for reward @a reward_id. * * @param cls closure, typically a connection to the db * @param instance_id which instance should we lookup reward details for * @param reward_id which reward should we lookup details on * @param[out] total_authorized amount how high is the reward (with fees) * @param[out] total_picked_up how much of the reward was so far picked up (with fees) * @param[out] expiration set to when the reward expires * @param[out] exchange_url set to the exchange URL where the reserve is * @param[out] next_url set to the URL where the wallet should navigate after getting the reward * @param[out] reserve_priv set to private key of reserve to be debited * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_reward (void *cls, const char *instance_id, const struct TALER_RewardIdentifierP *reward_id, struct TALER_Amount *total_authorized, struct TALER_Amount *total_picked_up, struct GNUNET_TIME_Timestamp *expiration, char **exchange_url, char **next_url, struct TALER_ReservePrivateKeyP *reserve_priv) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (reward_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_RESULT_SPEC_AMOUNT ("amount", total_authorized), TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up", total_picked_up), GNUNET_PQ_result_spec_timestamp ("expiration", expiration), GNUNET_PQ_result_spec_string ("exchange_url", exchange_url), GNUNET_PQ_result_spec_string ("next_url", next_url), GNUNET_PQ_result_spec_auto_from_type ("reserve_priv", reserve_priv), GNUNET_PQ_result_spec_end }; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_reward", params, rs); } /** * Context used for postgres_lookup_rewards(). */ struct LookupMerchantRewardsContext { /** * Postgres context. */ struct PostgresClosure *pg; /** * Function to call with the results. */ TALER_MERCHANTDB_RewardsCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Internal result. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about rewards. * * @param[in,out] cls of type `struct LookupRewardsContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_rewards_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupMerchantRewardsContext *plc = cls; struct PostgresClosure *pg = plc->pg; for (unsigned int i = 0; i < num_results; i++) { uint64_t row_id; struct TALER_RewardIdentifierP reward_id; struct TALER_Amount reward_amount; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("reward_serial", &row_id), GNUNET_PQ_result_spec_auto_from_type ("reward_id", &reward_id), TALER_PQ_RESULT_SPEC_AMOUNT ("amount", &reward_amount), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); plc->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } plc->cb (plc->cb_cls, row_id, reward_id, reward_amount); GNUNET_PQ_cleanup_result (rs); } } /** * Lookup rewards * * @param cls closure, typically a connection to the db * @param instance_id which instance should we lookup rewards for * @param expired should we include expired rewards? * @param limit maximum number of results to return, positive for * ascending row id, negative for descending * @param offset row id to start returning results from * @param cb function to call with reward data * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus postgres_lookup_rewards (void *cls, const char *instance_id, enum TALER_EXCHANGE_YesNoAll expired, int64_t limit, uint64_t offset, TALER_MERCHANTDB_RewardsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupMerchantRewardsContext plc = { .pg = pg, .cb = cb, .cb_cls = cb_cls }; uint64_t ulimit = (limit > 0) ? limit : -limit; uint8_t bexpired; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&ulimit), GNUNET_PQ_query_param_uint64 (&offset), GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_auto_from_type (&bexpired), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; char stmt[128]; bexpired = (TALER_EXCHANGE_YNA_YES == expired); GNUNET_snprintf (stmt, sizeof (stmt), "lookup_rewards_%s%s", (limit > 0) ? "inc" : "dec", (TALER_EXCHANGE_YNA_ALL == expired) ? "" : "_expired"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, stmt, params, &lookup_rewards_cb, &plc); if (0 != plc.qs) return plc.qs; return qs; } /** * Closure for #lookup_pickup_details_cb(). */ struct LookupRewardDetailsContext { /** * Length of the @e sigs array */ unsigned int *pickups_length; /** * Where to store the signatures. */ struct TALER_MERCHANTDB_PickupDetails **pickups; /** * Database handle. */ struct PostgresClosure *pg; /** * Transaction status. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about pickups. * * @param[in,out] cls of type `struct LookupRewardDetailsContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_pickup_details_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupRewardDetailsContext *ltdc = cls; struct PostgresClosure *pg = ltdc->pg; *ltdc->pickups_length = num_results; *ltdc->pickups = GNUNET_new_array (num_results, struct TALER_MERCHANTDB_PickupDetails); for (unsigned int i = 0; i < num_results; i++) { struct TALER_MERCHANTDB_PickupDetails *pd = &((*ltdc->pickups)[i]); uint64_t num_planchets = 0; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("pickup_id", &pd->pickup_id), TALER_PQ_RESULT_SPEC_AMOUNT ("amount", &pd->requested_amount), GNUNET_PQ_result_spec_uint64 ("num_planchets", &num_planchets), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR; GNUNET_array_grow (*ltdc->pickups, *ltdc->pickups_length, 0); return; } pd->num_planchets = num_planchets; } } /** * Lookup reward details for reward @a reward_id. * * @param cls closure, typically a connection to the db * @param instance_id which instance should we lookup reward details for * @param reward_id which reward should we lookup details on * @param fpu should we fetch details about individual pickups * @param[out] total_authorized amount how high is the reward (with fees) * @param[out] total_picked_up how much of the reward was so far picked up (with fees) * @param[out] justification why was the reward approved * @param[out] expiration set to when the reward expires * @param[out] reserve_pub set to which reserve is debited * @param[out] pickups_length set to the length of @e pickups * @param[out] pickups if @a fpu is true, set to details about the pickup operations * @return transaction status, */ static enum GNUNET_DB_QueryStatus postgres_lookup_reward_details (void *cls, const char *instance_id, const struct TALER_RewardIdentifierP *reward_id, bool fpu, struct TALER_Amount *total_authorized, struct TALER_Amount *total_picked_up, char **justification, struct GNUNET_TIME_Timestamp *expiration, struct TALER_ReservePublicKeyP *reserve_pub, unsigned int *pickups_length, struct TALER_MERCHANTDB_PickupDetails **pickups) { struct PostgresClosure *pg = cls; uint64_t reward_serial; enum GNUNET_DB_QueryStatus qs; { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (reward_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("reward_serial", &reward_serial), TALER_PQ_RESULT_SPEC_AMOUNT ("amount", total_authorized), TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up", total_picked_up), GNUNET_PQ_result_spec_string ("justification", justification), GNUNET_PQ_result_spec_timestamp ("expiration", expiration), GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", reserve_pub), GNUNET_PQ_result_spec_end }; check_connection (pg); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_reward_details", params, rs); if (qs <= 0) return qs; if (! fpu) { *pickups_length = 0; *pickups = NULL; return qs; } } { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&reward_serial), GNUNET_PQ_query_param_end }; struct LookupRewardDetailsContext ltdc = { .pickups_length = pickups_length, .pickups = pickups, .pg = pg, .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT }; qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_pickup_details", params, &lookup_pickup_details_cb, <dc); if (qs < 0) return qs; return ltdc.qs; } } /** * Insert blind signature obtained from the exchange during a * reward pickup operation. * * @param cls closure, typically a connection to the db * @param pickup_id unique ID for the operation * @param offset offset of the blind signature for the pickup * @param blind_sig the blind signature * @return transaction status, usually * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known */ static enum GNUNET_DB_QueryStatus postgres_insert_pickup_blind_signature ( void *cls, const struct TALER_PickupIdentifierP *pickup_id, uint32_t offset, const struct TALER_BlindedDenominationSignature *blind_sig) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (pickup_id), GNUNET_PQ_query_param_uint32 (&offset), TALER_PQ_query_param_blinded_denom_sig (blind_sig), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_pickup_blind_signature", params); } /** * Delete information about a template. * * @param cls closure * @param instance_id instance to delete template of * @param template_id template to delete * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS * if template unknown. */ static enum GNUNET_DB_QueryStatus postgres_delete_template (void *cls, const char *instance_id, const char *template_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (template_id), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "delete_template", params); } /** * Insert details about a particular template. * * @param cls closure * @param instance_id instance to insert template for * @param template_id template identifier of template to insert * @param td the template details to insert * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_insert_template (void *cls, const char *instance_id, const char *template_id, const struct TALER_MERCHANTDB_TemplateDetails *td) { struct PostgresClosure *pg = cls; uint32_t pos32 = (uint32_t) td->pos_algorithm; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (template_id), GNUNET_PQ_query_param_string (td->template_description), (NULL == td->pos_key) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (td->pos_key), GNUNET_PQ_query_param_uint32 (&pos32), TALER_PQ_query_param_json (td->template_contract), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_template", params); } /** * Update details about a particular template. * * @param cls closure * @param instance_id instance to update template for * @param template_id template to update * @param td update to the template details on success, can be NULL * (in that case we only want to check if the template exists) * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template * does not yet exist. */ static enum GNUNET_DB_QueryStatus postgres_update_template (void *cls, const char *instance_id, const char *template_id, const struct TALER_MERCHANTDB_TemplateDetails *td) { struct PostgresClosure *pg = cls; uint32_t pos32 = (uint32_t) td->pos_algorithm; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (template_id), GNUNET_PQ_query_param_string (td->template_description), (NULL == td->pos_key) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (td->pos_key), GNUNET_PQ_query_param_uint32 (&pos32), TALER_PQ_query_param_json (td->template_contract), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "update_template", params); } /** * Context used for postgres_lookup_template(). */ struct LookupTemplateContext { /** * Function to call with the results. */ TALER_MERCHANTDB_TemplatesCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Did database result extraction fail? */ bool extract_failed; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about template. * * @param[in,out] cls of type `struct LookupTemplateContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_templates_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupTemplateContext *tlc = cls; for (unsigned int i = 0; i < num_results; i++) { char *template_id; char *template_description; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("template_id", &template_id), GNUNET_PQ_result_spec_string ("template_description", &template_description), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); tlc->extract_failed = true; return; } tlc->cb (tlc->cb_cls, template_id, template_description); GNUNET_PQ_cleanup_result (rs); } } /** * Lookup all of the templates the given instance has configured. * * @param cls closure * @param instance_id instance to lookup template for * @param cb function to call on all template found * @param cb_cls closure for @a cb * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_lookup_templates (void *cls, const char *instance_id, TALER_MERCHANTDB_TemplatesCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupTemplateContext tlc = { .cb = cb, .cb_cls = cb_cls, /* Can be overwritten by the lookup_template_cb */ .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_templates", params, &lookup_templates_cb, &tlc); /* If there was an error inside lookup_template_cb, return a hard error. */ if (tlc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; } /** * Lookup details about a particular template. * * @param cls closure * @param instance_id instance to lookup template for * @param template_id template to lookup * @param[out] td set to the template details on success, can be NULL * (in that case we only want to check if the template exists) * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_lookup_template (void *cls, const char *instance_id, const char *template_id, struct TALER_MERCHANTDB_TemplateDetails *td) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (template_id), GNUNET_PQ_query_param_end }; if (NULL == td) { struct GNUNET_PQ_ResultSpec rs_null[] = { GNUNET_PQ_result_spec_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_template", params, rs_null); } else { uint32_t pos32; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("template_description", &td->template_description), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("pos_key", &td->pos_key), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_uint32 ("pos_algorithm", &pos32), NULL), TALER_PQ_result_spec_json ("template_contract", &td->template_contract), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); td->pos_key = NULL; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_template", params, rs); td->pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32; return qs; } } /** * Delete information about a webhook. * * @param cls closure * @param instance_id instance to delete webhook of * @param webhook_id webhook to delete * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS * if webhook unknown. */ static enum GNUNET_DB_QueryStatus postgres_delete_webhook (void *cls, const char *instance_id, const char *webhook_id) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (webhook_id), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "delete_webhook", params); } /** * Insert details about a particular webhook. * * @param cls closure * @param instance_id instance to insert webhook for * @param webhook_id webhook identifier of webhook to insert * @param wb the webhook details to insert * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_insert_webhook (void *cls, const char *instance_id, const char *webhook_id, const struct TALER_MERCHANTDB_WebhookDetails *wb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (webhook_id), GNUNET_PQ_query_param_string (wb->event_type), GNUNET_PQ_query_param_string (wb->url), GNUNET_PQ_query_param_string (wb->http_method), (NULL == wb->header_template) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (wb->header_template), (NULL == wb->body_template) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (wb->body_template), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_webhook", params); } /** * Update details about a particular webhook. * * @param cls closure * @param instance_id instance to update template for * @param webhook_id webhook to update * @param wb update to the webhook details on success, can be NULL * (in that case we only want to check if the webhook exists) * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the webhook * does not yet exist. */ static enum GNUNET_DB_QueryStatus postgres_update_webhook (void *cls, const char *instance_id, const char *webhook_id, const struct TALER_MERCHANTDB_WebhookDetails *wb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (webhook_id), GNUNET_PQ_query_param_string (wb->event_type), GNUNET_PQ_query_param_string (wb->url), GNUNET_PQ_query_param_string (wb->http_method), (NULL == wb->header_template) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (wb->header_template), (NULL == wb->body_template) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (wb->body_template), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "update_webhook", params); } /** * Context used for postgres_lookup_webhook(). */ struct LookupWebhookContext { /** * Function to call with the results. */ TALER_MERCHANTDB_WebhooksCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Did database result extraction fail? */ bool extract_failed; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about webhook. * * @param[in,out] cls of type `struct LookupWebhookContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_webhooks_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupWebhookContext *wlc = cls; for (unsigned int i = 0; i < num_results; i++) { char *webhook_id; char *event_type; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("webhook_id", &webhook_id), GNUNET_PQ_result_spec_string ("event_type", &event_type), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); wlc->extract_failed = true; return; } wlc->cb (wlc->cb_cls, webhook_id, event_type); GNUNET_PQ_cleanup_result (rs); } } /** * Lookup all of the webhooks the given instance has configured. * * @param cls closure * @param instance_id instance to lookup webhook for * @param cb function to call on all webhook found * @param cb_cls closure for @a cb * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_lookup_webhooks (void *cls, const char *instance_id, TALER_MERCHANTDB_WebhooksCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupWebhookContext wlc = { .cb = cb, .cb_cls = cb_cls, /* Can be overwritten by the lookup_webhook_cb */ .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_webhooks", params, &lookup_webhooks_cb, &wlc); /* If there was an error inside lookup_webhook_cb, return a hard error. */ if (wlc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; } /** * Lookup details about a particular webhook. * * @param cls closure * @param instance_id instance to lookup webhook for * @param webhook_id webhook to lookup * @param[out] wb set to the webhook details on success, can be NULL * (in that case we only want to check if the webhook exists) * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_lookup_webhook (void *cls, const char *instance_id, const char *webhook_id, struct TALER_MERCHANTDB_WebhookDetails *wb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (webhook_id), GNUNET_PQ_query_param_end }; if (NULL == wb) { struct GNUNET_PQ_ResultSpec rs_null[] = { GNUNET_PQ_result_spec_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_webhook", params, rs_null); } else { struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("event_type", &wb->event_type), GNUNET_PQ_result_spec_string ("url", &wb->url), GNUNET_PQ_result_spec_string ("http_method", &wb->http_method), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("header_template", &wb->header_template), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("body_template", &wb->body_template), NULL), GNUNET_PQ_result_spec_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_webhook", params, rs); } } /** * Context used for postgres_lookup_webhook(). */ struct LookupWebhookDetailContext { /** * Function to call with the results. */ TALER_MERCHANTDB_WebhookDetailCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Did database result extraction fail? */ bool extract_failed; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about webhook. * * @param[in,out] cls of type `struct LookupPendingWebhookContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_webhook_by_event_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupWebhookDetailContext *wlc = cls; for (unsigned int i = 0; i < num_results; i++) { uint64_t webhook_serial; char *event_type; char *url; char *http_method; char *header_template; char *body_template; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("webhook_serial", &webhook_serial), GNUNET_PQ_result_spec_string ("event_type", &event_type), GNUNET_PQ_result_spec_string ("url", &url), GNUNET_PQ_result_spec_string ("http_method", &http_method), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("header_template", &header_template), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("body_template", &body_template), NULL), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); wlc->extract_failed = true; return; } wlc->cb (wlc->cb_cls, webhook_serial, event_type, url, http_method, header_template, body_template); GNUNET_PQ_cleanup_result (rs); } } /** * Lookup webhook by event * * @param cls closure * @param instance_id instance to lookup webhook for * @param event_type event that we need to put in the pending webhook * @param[out] cb set to the webhook details on success * @param cb_cls callback closure * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_lookup_webhook_by_event (void *cls, const char *instance_id, const char *event_type, TALER_MERCHANTDB_WebhookDetailCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupWebhookDetailContext wlc = { .cb = cb, .cb_cls = cb_cls, .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (event_type), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_webhook_by_event", params, &lookup_webhook_by_event_cb, &wlc); if (wlc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; } /** * Insert webhook in the pending webhook. * * @param cls closure * @param instance_id instance to insert webhook for * @param webhook_serial webhook to insert in the pending webhook * @param url to make the request to * @param http_method for the webhook * @param header of the webhook * @param body of the webhook * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_insert_pending_webhook (void *cls, const char *instance_id, uint64_t webhook_serial, const char *url, const char *http_method, const char *header, const char *body) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&webhook_serial), GNUNET_PQ_query_param_string (url), GNUNET_PQ_query_param_string (http_method), NULL == header ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (header), NULL == body ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string (body), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_pending_webhook", params); } /** * Context used for postgres_lookup_future_webhook(). */ struct LookupPendingWebhookContext { /** * Function to call with the results. */ TALER_MERCHANTDB_PendingWebhooksCallback cb; /** * Closure for @a cb. */ void *cb_cls; /** * Did database result extraction fail? */ bool extract_failed; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about webhook. * * @param[in,out] cls of type `struct LookupPendingWebhookContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_pending_webhooks_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupPendingWebhookContext *pwlc = cls; for (unsigned int i = 0; i < num_results; i++) { uint64_t webhook_pending_serial; struct GNUNET_TIME_Absolute next_attempt; uint32_t retries; char *url; char *http_method; char *header = NULL; char *body = NULL; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("webhook_pending_serial", &webhook_pending_serial), GNUNET_PQ_result_spec_absolute_time ("next_attempt", &next_attempt), GNUNET_PQ_result_spec_uint32 ("retries", &retries), GNUNET_PQ_result_spec_string ("url", &url), GNUNET_PQ_result_spec_string ("http_method", &http_method), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("header", &header), NULL), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("body", &body), NULL), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); pwlc->extract_failed = true; return; } pwlc->cb (pwlc->cb_cls, webhook_pending_serial, next_attempt, retries, url, http_method, header, body); GNUNET_PQ_cleanup_result (rs); } } /** * Lookup the webhook that need to be send in priority. * send. * * @param cls closure * @param cb pending webhook callback * @param cb_cls callback closure */ // WHERE next_attempt <= now ORDER BY next_attempt ASC static enum GNUNET_DB_QueryStatus postgres_lookup_pending_webhooks (void *cls, TALER_MERCHANTDB_PendingWebhooksCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupPendingWebhookContext pwlc = { .cb = cb, .cb_cls = cb_cls, .extract_failed = false, }; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params_null[] = { GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_pending_webhooks", params_null, &lookup_pending_webhooks_cb, &pwlc); if (pwlc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; } /** * Lookup future webhook in the pending webhook that need to be send. * With that we can know how long the system can 'sleep'. * * @param cls closure * @param cb pending webhook callback * @param cb_cls callback closure */ // ORDER BY next_attempt ASC LIMIT 1 static enum GNUNET_DB_QueryStatus postgres_lookup_future_webhook (void *cls, TALER_MERCHANTDB_PendingWebhooksCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupPendingWebhookContext pwlc = { .cb = cb, .cb_cls = cb_cls, .extract_failed = false, }; struct GNUNET_PQ_QueryParam params_null[] = { GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_future_webhook", params_null, &lookup_pending_webhooks_cb, &pwlc); if (pwlc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; } /** * Lookup all the webhooks in the pending webhook. * Use by the administrator * * @param cls closure * @param instance_id to lookup webhooks for this instance particularly * @param min_row to see the list of the pending webhook that it is started with this minimum row. * @param max_results to see the list of the pending webhook that it is end with this max results. * @param cb pending webhook callback * @param cb_cls callback closure */ // WHERE webhook_pending_serial > min_row ORDER BY webhook_pending_serial ASC LIMIT max_results static enum GNUNET_DB_QueryStatus postgres_lookup_all_webhooks (void *cls, const char *instance_id, uint64_t min_row, uint32_t max_results, TALER_MERCHANTDB_PendingWebhooksCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupPendingWebhookContext pwlc = { .cb = cb, .cb_cls = cb_cls, .extract_failed = false, }; uint64_t max_results64 = max_results; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&min_row), GNUNET_PQ_query_param_uint64 (&max_results64), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_all_webhooks", params, &lookup_pending_webhooks_cb, &pwlc); if (pwlc.extract_failed) return GNUNET_DB_STATUS_HARD_ERROR; return qs; } /** * Update the pending webhook. It is use if the webhook can't be send. * * @param cls closure * @param webhook_pending_serial pending_webhook that need to be update * @param next_attempt when to try the webhook next * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_update_pending_webhook (void *cls, uint64_t webhook_pending_serial, struct GNUNET_TIME_Absolute next_attempt) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&webhook_pending_serial), GNUNET_PQ_query_param_absolute_time (&next_attempt), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "update_pending_webhook", params); } /** * Delete a webhook in the pending webhook after the * webhook was completed successfully. * * @param cls closure * @param webhook_pending_serial identifies the row that needs to be deleted in the pending webhook table * @return database result code */ static enum GNUNET_DB_QueryStatus postgres_delete_pending_webhook (void *cls, uint64_t webhook_pending_serial) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&webhook_pending_serial), GNUNET_PQ_query_param_end }; check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "delete_pending_webhook", params); } /** * Establish connection to the database. * * @param cls plugin context * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ static int postgres_connect (void *cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_PreparedStatement ps[] = { GNUNET_PQ_make_prepare ("end_transaction", "COMMIT"), /* for postgres_lookup_deposits_by_order() */ GNUNET_PQ_make_prepare ("lookup_deposits_by_order", "SELECT" " deposit_serial" ",exchange_url" ",h_wire" ",amount_with_fee" ",deposit_fee" ",coin_pub" " FROM merchant_deposits" " JOIN merchant_accounts USING (account_serial)" " WHERE order_serial=$1"), /* for postgres_lookup_transfer_details_by_order() */ GNUNET_PQ_make_prepare ("lookup_transfer_details_by_order", "SELECT" " md.deposit_serial" ",md.exchange_url" ",mt.wtid" ",exchange_deposit_value" ",exchange_deposit_fee" ",deposit_timestamp" ",mt.confirmed AS transfer_confirmed" " FROM merchant_transfer_to_coin" " JOIN merchant_deposits AS md USING (deposit_serial)" " JOIN merchant_transfers AS mt USING (credit_serial)" " WHERE deposit_serial IN" " (SELECT deposit_serial" " FROM merchant_deposits" " WHERE order_serial=$1)"), /* for postgres_mark_order_wired() */ GNUNET_PQ_make_prepare ("mark_order_wired", "UPDATE merchant_contract_terms SET" " wired=true" " WHERE order_serial=$1"), /* for postgres_lookup_refunds_detailed() */ GNUNET_PQ_make_prepare ("lookup_refunds_detailed", "SELECT" " refund_serial" ",refund_timestamp" ",coin_pub" ",merchant_deposits.exchange_url" ",rtransaction_id" ",reason" ",refund_amount" ",merchant_refund_proofs.exchange_sig IS NULL AS pending" " FROM merchant_refunds" " JOIN merchant_deposits USING (order_serial, coin_pub)" " LEFT JOIN merchant_refund_proofs USING (refund_serial)" " WHERE order_serial=" " (SELECT order_serial" " FROM merchant_contract_terms" " WHERE h_contract_terms=$2" " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1))"), /* for postgres_insert_refund_proof() */ GNUNET_PQ_make_prepare ("insert_refund_proof", "INSERT INTO merchant_refund_proofs" "(refund_serial" ",exchange_sig" ",signkey_serial)" "SELECT $1, $2, signkey_serial" " FROM merchant_exchange_signing_keys" " WHERE exchange_pub=$3" " ORDER BY start_date DESC" " LIMIT 1"), /* for postgres_lookup_refund_proof() */ GNUNET_PQ_make_prepare ("lookup_refund_proof", "SELECT" " merchant_exchange_signing_keys.exchange_pub" ",exchange_sig" " FROM merchant_refund_proofs" " JOIN merchant_exchange_signing_keys" " USING (signkey_serial)" " WHERE" " refund_serial=$1"), /* for postgres_lookup_order_by_fulfillment() */ GNUNET_PQ_make_prepare ("lookup_order_by_fulfillment", "SELECT" " order_id" " FROM merchant_contract_terms" " WHERE fulfillment_url=$2" " AND session_id=$3" " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* for postgres_delete_transfer() */ GNUNET_PQ_make_prepare ("delete_transfer", "DELETE FROM merchant_transfers" " WHERE" " credit_serial=$2" " AND account_serial IN " " (SELECT account_serial " " FROM merchant_accounts" " WHERE merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1))"), /* for postgres_check_transfer_exists() */ GNUNET_PQ_make_prepare ("check_transfer_exists", "SELECT" " 1" " FROM merchant_transfers" " JOIN merchant_accounts" " USING (account_serial)" " WHERE" " credit_serial=$2" " AND" " merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* for postgres_lookup_account() */ GNUNET_PQ_make_prepare ("lookup_account", "SELECT" " account_serial" " FROM merchant_accounts" " WHERE payto_uri=$2" " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* for postgres_lookup_wire_fee() */ GNUNET_PQ_make_prepare ("lookup_wire_fee", "SELECT" " wire_fee" ",closing_fee" ",start_date" ",end_date" ",master_sig" " FROM merchant_exchange_wire_fees" " WHERE master_pub=$1" " AND h_wire_method=$2" " AND start_date <= $3" " AND end_date > $3"), /* for postgres_lookup_deposits_by_contract_and_coin() */ GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin", "SELECT" " exchange_url" ",amount_with_fee" ",deposit_fee" ",refund_fee" ",wire_fee" ",h_wire" ",deposit_timestamp" ",refund_deadline" ",exchange_sig" ",exchange_pub" " FROM merchant_contract_terms" " JOIN merchant_deposits USING (order_serial)" " JOIN merchant_exchange_signing_keys USING (signkey_serial)" " JOIN merchant_accounts USING (account_serial)" " WHERE h_contract_terms=$2" " AND coin_pub=$3" " AND merchant_contract_terms.merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* for postgres_lookup_transfer() */ GNUNET_PQ_make_prepare ("lookup_transfer", "SELECT" " mt.credit_amount AS credit_amount" ",mts.credit_amount AS exchange_amount" ",wire_fee" ",execution_time" ",verified" " FROM merchant_transfers mt" " JOIN merchant_accounts USING (account_serial)" " JOIN merchant_instances USING (merchant_serial)" " LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)" " WHERE wtid=$2" " AND exchange_url=$1" " AND merchant_id=$3;"), /* for postgres_lookup_transfer_summary() */ GNUNET_PQ_make_prepare ("lookup_transfer_summary", "SELECT" " order_id" ",exchange_deposit_value" ",exchange_deposit_fee" " FROM merchant_transfers" " JOIN merchant_transfer_to_coin USING (credit_serial)" " JOIN merchant_deposits USING (deposit_serial)" " JOIN merchant_contract_terms USING (order_serial)" " WHERE wtid=$2" " AND merchant_transfers.exchange_url=$1"), /* for postgres_lookup_transfer_details() */ GNUNET_PQ_make_prepare ("lookup_transfer_details", "SELECT" " merchant_contract_terms.h_contract_terms" ",merchant_transfer_to_coin.offset_in_exchange_list" ",merchant_deposits.coin_pub" ",exchange_deposit_value" ",exchange_deposit_fee" " FROM merchant_transfer_to_coin" " JOIN merchant_deposits USING (deposit_serial)" " JOIN merchant_contract_terms USING (order_serial)" " JOIN merchant_transfers USING (credit_serial)" " WHERE merchant_transfers.wtid=$2" " AND merchant_transfers.exchange_url=$1"), /* For postgres_insert_reserve() */ GNUNET_PQ_make_prepare ("insert_reserve_key", "INSERT INTO merchant_reward_reserve_keys" "(reserve_serial" ",reserve_priv" ",exchange_url" ",master_pub" ")" "SELECT reserve_serial, $3, $4, $5" " FROM merchant_reward_reserves" " WHERE reserve_pub=$2" " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* For postgres_lookup_pending_reserves() */ GNUNET_PQ_make_prepare ("lookup_pending_reserves", "SELECT" " reserve_pub" ",merchant_id" ",exchange_url" ",merchant_initial_balance" " FROM merchant_reward_reserves" " JOIN merchant_instances USING (merchant_serial)" " JOIN merchant_reward_reserve_keys USING (reserve_serial)" " WHERE exchange_initial_balance=CAST((0,0) AS taler_amount)"), /* For postgres_lookup_reserve() */ GNUNET_PQ_make_prepare ("lookup_reserve", "SELECT" " creation_time" ",expiration" ",merchant_initial_balance" ",exchange_initial_balance" ",rewards_committed" ",rewards_picked_up" ",reserve_priv IS NOT NULL AS active" ",exchange_url" ",master_pub" " FROM merchant_reward_reserves" " FULL OUTER JOIN merchant_reward_reserve_keys USING (reserve_serial)" " WHERE reserve_pub = $2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* For postgres_lookup_reserve() */ GNUNET_PQ_make_prepare ("lookup_reserve_rewards", "SELECT" " justification" ",reward_id" ",amount" " FROM merchant_rewards" " WHERE reserve_serial =" " (SELECT reserve_serial" " FROM merchant_reward_reserves" " WHERE reserve_pub=$2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1))"), /* for postgres_delete_reserve() */ GNUNET_PQ_make_prepare ("delete_reserve", "DELETE" " FROM merchant_reward_reserve_keys" " WHERE reserve_serial=" " (SELECT reserve_serial" " FROM merchant_reward_reserves" " WHERE reserve_pub=$2" " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1))"), /* for postgres_purge_reserve() */ GNUNET_PQ_make_prepare ("purge_reserve", "DELETE" " FROM merchant_reward_reserves" " WHERE reserve_pub=$2" " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* For postgres_lookup_pickup() */ GNUNET_PQ_make_prepare ("lookup_pickup", "SELECT" " exchange_url" ",reserve_priv" ",pickup_serial" " FROM merchant_reward_pickups" " JOIN merchant_rewards USING (reward_serial)" " JOIN merchant_reward_reserves USING (reserve_serial)" " JOIN merchant_reward_reserve_keys USING (reserve_serial)" " WHERE pickup_id = $3" " AND reward_id = $2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* For postgres_lookup_pickup() */ GNUNET_PQ_make_prepare ("lookup_pickup_signatures", "SELECT" " coin_offset" ",blind_sig" " FROM merchant_reward_pickup_signatures" " WHERE pickup_serial = $1"), /* For postgres_lookup_reward() */ GNUNET_PQ_make_prepare ("lookup_reward", "SELECT" " amount" ",picked_up" ",merchant_rewards.expiration" ",exchange_url" ",next_url" ",reserve_priv" " FROM merchant_rewards" " JOIN merchant_reward_reserves USING (reserve_serial)" " JOIN merchant_reward_reserve_keys USING (reserve_serial)" " WHERE reward_id = $2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* For postgres_lookup_reward() */ GNUNET_PQ_make_prepare ("lookup_rewards_inc", "SELECT" " reward_serial" ",reward_id" ",amount" ",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */ ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ " FROM merchant_rewards" " JOIN merchant_reward_reserves USING (reserve_serial)" " WHERE merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " reward_serial > $3" " ORDER BY reward_serial ASC" " LIMIT $2"), GNUNET_PQ_make_prepare ("lookup_rewards_dec", "SELECT" " reward_serial" ",reward_id" ",amount" ",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */ ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */ " FROM merchant_rewards" " JOIN merchant_reward_reserves USING (reserve_serial)" " WHERE merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " reward_serial < $3" " ORDER BY reward_serial DESC" " LIMIT $2"), GNUNET_PQ_make_prepare ("lookup_rewards_inc_expired", "SELECT" " reward_serial" ",reward_id" ",amount" " FROM merchant_rewards" " JOIN merchant_reward_reserves USING (reserve_serial)" " WHERE merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " reward_serial > $3" " AND" " CAST($5 as BOOL) = (merchant_rewards.expiration < $4)" " ORDER BY reward_serial ASC" " LIMIT $2"), GNUNET_PQ_make_prepare ("lookup_rewards_dec_expired", "SELECT" " reward_serial" ",reward_id" ",amount" " FROM merchant_rewards" " JOIN merchant_reward_reserves USING (reserve_serial)" " WHERE merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)" " AND" " reward_serial < $3" " AND" " CAST($5 as BOOL) = (merchant_rewards.expiration < $4)" " ORDER BY reward_serial DESC" " LIMIT $2"), /* for postgres_lookup_reward_details() */ GNUNET_PQ_make_prepare ("lookup_reward_details", "SELECT" " reward_serial" ",amount" ",picked_up" ",justification" ",merchant_rewards.expiration" ",reserve_pub" " FROM merchant_rewards" " JOIN merchant_reward_reserves USING (reserve_serial)" " WHERE reward_id = $2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), /* for postgres_lookup_reward_details() */ GNUNET_PQ_make_prepare ("lookup_pickup_details", "SELECT" " pickup_id" ",amount" ",COUNT(blind_sig) AS num_planchets" " FROM merchant_reward_pickups" " JOIN merchant_reward_pickup_signatures USING (pickup_serial)" " WHERE reward_serial = $1" " GROUP BY pickup_serial"), /* for postgres_insert_pickup_blind_signature() */ GNUNET_PQ_make_prepare ("insert_pickup_blind_signature", "INSERT INTO merchant_reward_pickup_signatures" "(pickup_serial" ",coin_offset" ",blind_sig" ") " "SELECT" " pickup_serial, $2, $3" " FROM merchant_reward_pickups" " WHERE pickup_id=$1"), /* for postgres_lookup_templates() */ GNUNET_PQ_make_prepare ("lookup_templates", "SELECT" " template_id" ",template_description" " FROM merchant_template" " JOIN merchant_instances" " USING (merchant_serial)" " WHERE merchant_instances.merchant_id=$1"), /* for postgres_lookup_template() */ GNUNET_PQ_make_prepare ("lookup_template", "SELECT" " template_description" ",pos_key" ",pos_algorithm" ",template_contract" " FROM merchant_template" " JOIN merchant_instances" " USING (merchant_serial)" " WHERE merchant_instances.merchant_id=$1" " AND merchant_template.template_id=$2"), /* for postgres_delete_template() */ GNUNET_PQ_make_prepare ("delete_template", "DELETE" " FROM merchant_template" " WHERE merchant_template.merchant_serial=" " (SELECT merchant_serial " " FROM merchant_instances" " WHERE merchant_id=$1)" " AND merchant_template.template_id=$2"), /* for postgres_insert_template() */ GNUNET_PQ_make_prepare ("insert_template", "INSERT INTO merchant_template" "(merchant_serial" ",template_id" ",template_description" ",pos_key" ",pos_algorithm" ",template_contract" ")" " SELECT merchant_serial," " $2, $3, $4, $5, $6" " FROM merchant_instances" " WHERE merchant_id=$1"), /* for postgres_update_template() */ GNUNET_PQ_make_prepare ("update_template", "UPDATE merchant_template SET" " template_description=$3" ",pos_key=$4" ",pos_algorithm=$5" ",template_contract=$6" " WHERE merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)" " AND template_id=$2"), /* for postgres_lookup_webhooks() */ GNUNET_PQ_make_prepare ("lookup_webhooks", "SELECT" " webhook_id" ",event_type" " FROM merchant_webhook" " JOIN merchant_instances" " USING (merchant_serial)" " WHERE merchant_instances.merchant_id=$1"), /* for postgres_lookup_webhook() */ GNUNET_PQ_make_prepare ("lookup_webhook", "SELECT" " event_type" ",url" ",http_method" ",header_template" ",body_template" " FROM merchant_webhook" " JOIN merchant_instances" " USING (merchant_serial)" " WHERE merchant_instances.merchant_id=$1" " AND merchant_webhook.webhook_id=$2"), /* for postgres_delete_webhook() */ GNUNET_PQ_make_prepare ("delete_webhook", "DELETE" " FROM merchant_webhook" " WHERE merchant_webhook.merchant_serial=" " (SELECT merchant_serial " " FROM merchant_instances" " WHERE merchant_id=$1)" " AND merchant_webhook.webhook_id=$2"), /* for postgres_insert_webhook() */ GNUNET_PQ_make_prepare ("insert_webhook", "INSERT INTO merchant_webhook" "(merchant_serial" ",webhook_id" ",event_type" ",url" ",http_method" ",header_template" ",body_template" ")" " SELECT merchant_serial," " $2, $3, $4, $5, $6, $7" " FROM merchant_instances" " WHERE merchant_id=$1"), /* for postgres_update_webhook() */ GNUNET_PQ_make_prepare ("update_webhook", "UPDATE merchant_webhook SET" " event_type=$3" ",url=$4" ",http_method=$5" ",header_template=$6" ",body_template=$7" " WHERE merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)" " AND webhook_id=$2"), /* for postgres_lookup_webhook_by_event() */ GNUNET_PQ_make_prepare ("lookup_webhook_by_event", "SELECT" " webhook_serial" ",event_type" ",url" ",http_method" ",header_template" ",body_template" " FROM merchant_webhook" " JOIN merchant_instances" " USING (merchant_serial)" " WHERE merchant_instances.merchant_id=$1" " AND event_type=$2"), /* for postgres_delete_pending_webhook() */ GNUNET_PQ_make_prepare ("delete_pending_webhook", "DELETE" " FROM merchant_pending_webhooks" " WHERE webhook_pending_serial=$1"), /* for postgres_insert_pending_webhook() */ GNUNET_PQ_make_prepare ("insert_pending_webhook", "INSERT INTO merchant_pending_webhooks" "(merchant_serial" ",webhook_serial" ",url" ",http_method" ",header" ",body" ")" " SELECT mi.merchant_serial," " $2, $3, $4, $5, $6" " FROM merchant_instances mi" " WHERE mi.merchant_id=$1"), /* for postgres_update_pending_webhook() */ GNUNET_PQ_make_prepare ("update_pending_webhook", "UPDATE merchant_pending_webhooks SET" " retries=retries+1" ",next_attempt=$2" " WHERE webhook_pending_serial=$1"), /* for postgres_lookup_pending_webhooks() */ GNUNET_PQ_make_prepare ("lookup_pending_webhooks", "SELECT" " webhook_pending_serial" ",next_attempt" ",retries" ",url" ",http_method" ",header" ",body" " FROM merchant_pending_webhooks" " WHERE next_attempt <= $1" " ORDER BY next_attempt ASC" ), /* for postgres_lookup_future_webhook() */ GNUNET_PQ_make_prepare ("lookup_future_webhook", "SELECT" " webhook_pending_serial" ",next_attempt" ",retries" ",url" ",http_method" ",header" ",body" " FROM merchant_pending_webhooks" " ORDER BY next_attempt ASC LIMIT 1" ), /* for postgres_lookup_all_webhooks() */ GNUNET_PQ_make_prepare ("lookup_all_webhooks", " SELECT" " webhook_pending_serial" ",next_attempt" ",retries" ",url" ",http_method" ",header" ",body" " FROM merchant_pending_webhooks" " JOIN merchant_instances" " USING (merchant_serial)" " WHERE merchant_instances.merchant_id=$1" " AND webhook_pending_serial > $2" " ORDER BY webhook_pending_serial" " ASC LIMIT $3"), GNUNET_PQ_PREPARED_STATEMENT_END }; struct GNUNET_PQ_ExecuteStatement es[] = { GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"), GNUNET_PQ_EXECUTE_STATEMENT_END }; pg->conn = GNUNET_PQ_connect_with_cfg (pg->cfg, "merchantdb-postgres", NULL, es, ps); pg->prep_gen++; if (NULL == pg->conn) return GNUNET_SYSERR; return GNUNET_OK; }; /** * Initialize Postgres database subsystem. * * @param cls a configuration instance * @return NULL on error, otherwise a `struct TALER_MERCHANTDB_Plugin` */ void * libtaler_plugin_merchantdb_postgres_init (void *cls) { const struct GNUNET_CONFIGURATION_Handle *cfg = cls; struct PostgresClosure *pg; struct TALER_MERCHANTDB_Plugin *plugin; pg = GNUNET_new (struct PostgresClosure); pg->cfg = cfg; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "merchantdb-postgres", "SQL_DIR", &pg->sql_dir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "merchantdb-postgres", "SQL_DIR"); GNUNET_free (pg); return NULL; } if (GNUNET_OK != TALER_config_get_currency (cfg, &pg->currency)) { GNUNET_free (pg->sql_dir); GNUNET_free (pg); return NULL; } plugin = GNUNET_new (struct TALER_MERCHANTDB_Plugin); plugin->cls = pg; plugin->connect = &postgres_connect; plugin->create_tables = &postgres_create_tables; plugin->drop_tables = &postgres_drop_tables; plugin->event_listen = &postgres_event_listen; plugin->event_listen_cancel = &postgres_event_listen_cancel; plugin->event_notify = &postgres_event_notify; plugin->preflight = &postgres_preflight; plugin->start = &TMH_PG_start; plugin->start_read_committed = &TMH_PG_start_read_committed; plugin->rollback = &TMH_PG_rollback; plugin->commit = &TMH_PG_commit; plugin->lookup_instance_auth = &TMH_PG_lookup_instance_auth; plugin->insert_instance = &TMH_PG_insert_instance; plugin->insert_account = &TMH_PG_insert_account; plugin->update_account = &TMH_PG_update_account; plugin->account_kyc_set_status = &TMH_PG_account_kyc_set_status; plugin->account_kyc_get_status = &TMH_PG_account_kyc_get_status; plugin->delete_instance_private_key = &TMH_PG_delete_instance_private_key; plugin->purge_instance = &TMH_PG_purge_instance; plugin->update_instance = &TMH_PG_update_instance; plugin->update_instance_auth = &TMH_PG_update_instance_auth; plugin->activate_account = &TMH_PG_activate_account; plugin->inactivate_account = &TMH_PG_inactivate_account; plugin->update_transfer_status = &TMH_PG_update_transfer_status; plugin->lookup_products = &TMH_PG_lookup_products; plugin->lookup_product = &TMH_PG_lookup_product; plugin->delete_product = &TMH_PG_delete_product; plugin->insert_product = &TMH_PG_insert_product; plugin->update_product = &TMH_PG_update_product; plugin->lock_product = &TMH_PG_lock_product; plugin->expire_locks = &TMH_PG_expire_locks; plugin->delete_order = &TMH_PG_delete_order; plugin->lookup_order = &TMH_PG_lookup_order; plugin->lookup_order_summary = &TMH_PG_lookup_order_summary; plugin->lookup_orders = &TMH_PG_lookup_orders; plugin->insert_order = &TMH_PG_insert_order; plugin->unlock_inventory = &TMH_PG_unlock_inventory; plugin->insert_order_lock = &TMH_PG_insert_order_lock; plugin->lookup_contract_terms = &TMH_PG_lookup_contract_terms; plugin->lookup_contract_terms2 = &TMH_PG_lookup_contract_terms2; plugin->insert_contract_terms = &TMH_PG_insert_contract_terms; plugin->update_contract_terms = &TMH_PG_update_contract_terms; plugin->delete_contract_terms = &TMH_PG_delete_contract_terms; plugin->lookup_deposits = &TMH_PG_lookup_deposits; plugin->insert_exchange_signkey = &TMH_PG_insert_exchange_signkey; plugin->insert_deposit = &TMH_PG_insert_deposit; plugin->lookup_refunds = &TMH_PG_lookup_refunds; plugin->mark_contract_paid = &TMH_PG_mark_contract_paid; plugin->refund_coin = &TMH_PG_refund_coin; plugin->lookup_order_status = &TMH_PG_lookup_order_status; plugin->lookup_order_status_by_serial = &TMH_PG_lookup_order_status_by_serial; plugin->lookup_payment_status = &TMH_PG_lookup_payment_status; plugin->lookup_deposits_by_order = &postgres_lookup_deposits_by_order; plugin->lookup_transfer_details_by_order = &postgres_lookup_transfer_details_by_order; plugin->mark_order_wired = &postgres_mark_order_wired; plugin->increase_refund = &TMH_PG_increase_refund; plugin->lookup_refunds_detailed = &postgres_lookup_refunds_detailed; plugin->insert_refund_proof = &postgres_insert_refund_proof; plugin->lookup_refund_proof = &postgres_lookup_refund_proof; plugin->lookup_order_by_fulfillment = &postgres_lookup_order_by_fulfillment; plugin->delete_transfer = &postgres_delete_transfer; plugin->check_transfer_exists = &postgres_check_transfer_exists; plugin->lookup_account = &postgres_lookup_account; plugin->lookup_wire_fee = &postgres_lookup_wire_fee; plugin->lookup_deposits_by_contract_and_coin = &postgres_lookup_deposits_by_contract_and_coin; plugin->lookup_transfer = &postgres_lookup_transfer; plugin->set_transfer_status_to_confirmed = &TMH_PG_set_transfer_status_to_confirmed; plugin->lookup_transfer_summary = &postgres_lookup_transfer_summary; plugin->lookup_transfer_details = &postgres_lookup_transfer_details; plugin->lookup_instances = &TMH_PG_lookup_instances; plugin->lookup_instance = &TMH_PG_lookup_instance; plugin->lookup_transfers = &TMH_PG_lookup_transfers; plugin->update_wirewatch_progress = &TMH_PG_update_wirewatch_progress; plugin->select_wirewatch_accounts = &TMH_PG_select_wirewatch_accounts; plugin->lookup_reserves = &TMH_PG_lookup_reserves; plugin->lookup_pending_reserves = &postgres_lookup_pending_reserves; plugin->lookup_reserve = &postgres_lookup_reserve; plugin->delete_reserve = &postgres_delete_reserve; plugin->purge_reserve = &postgres_purge_reserve; plugin->lookup_pickup = &postgres_lookup_pickup; plugin->lookup_reward = &postgres_lookup_reward; plugin->lookup_rewards = &postgres_lookup_rewards; plugin->lookup_reward_details = &postgres_lookup_reward_details; plugin->insert_pickup_blind_signature = &postgres_insert_pickup_blind_signature; plugin->select_open_transfers = &TMH_PG_select_open_transfers; plugin->insert_exchange_keys = &TMH_PG_insert_exchange_keys; plugin->select_exchange_keys = &TMH_PG_select_exchange_keys; plugin->insert_deposit_to_transfer = &TMH_PG_insert_deposit_to_transfer; plugin->insert_transfer = &TMH_PG_insert_transfer; plugin->insert_transfer_details = &TMH_PG_insert_transfer_details; plugin->store_wire_fee_by_exchange = &TMH_PG_store_wire_fee_by_exchange; plugin->insert_reserve = &TMH_PG_insert_reserve; plugin->activate_reserve = &TMH_PG_activate_reserve; plugin->authorize_reward = &TMH_PG_authorize_reward; plugin->insert_pickup = &TMH_PG_insert_pickup; plugin->lookup_templates = &postgres_lookup_templates; plugin->lookup_template = &postgres_lookup_template; plugin->delete_template = &postgres_delete_template; plugin->insert_template = &postgres_insert_template; plugin->update_template = &postgres_update_template; plugin->lookup_webhooks = &postgres_lookup_webhooks; plugin->lookup_webhook = &postgres_lookup_webhook; plugin->delete_webhook = &postgres_delete_webhook; plugin->insert_webhook = &postgres_insert_webhook; plugin->update_webhook = &postgres_update_webhook; plugin->lookup_webhook_by_event = &postgres_lookup_webhook_by_event; plugin->lookup_all_webhooks = &postgres_lookup_all_webhooks; plugin->lookup_future_webhook = &postgres_lookup_future_webhook; plugin->lookup_pending_webhooks = &postgres_lookup_pending_webhooks; plugin->delete_pending_webhook = &postgres_delete_pending_webhook; plugin->insert_pending_webhook = &postgres_insert_pending_webhook; plugin->update_pending_webhook = &postgres_update_pending_webhook; plugin->delete_exchange_accounts = &TMH_PG_delete_exchange_accounts; plugin->select_accounts_by_exchange = &TMH_PG_select_accounts_by_exchange; plugin->insert_exchange_account = &TMH_PG_insert_exchange_account; return plugin; } /** * Shutdown Postgres database subsystem. * * @param cls a `struct TALER_MERCHANTDB_Plugin` * @return NULL (always) */ void * libtaler_plugin_merchantdb_postgres_done (void *cls) { struct TALER_MERCHANTDB_Plugin *plugin = cls; struct PostgresClosure *pg = plugin->cls; if (NULL != pg->conn) { GNUNET_PQ_disconnect (pg->conn); pg->conn = NULL; } GNUNET_free (pg->sql_dir); GNUNET_free (pg->currency); GNUNET_free (pg); GNUNET_free (plugin); return NULL; } /* end of plugin_merchantdb_postgres.c */