summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------contrib/wallet-core0
-rw-r--r--src/backenddb/Makefile.am12
-rw-r--r--src/backenddb/merchant-0001.sql105
-rw-r--r--src/backenddb/pg_activate_reserve.c56
-rw-r--r--src/backenddb/pg_activate_reserve.h48
-rw-r--r--src/backenddb/pg_authorize_reward.c422
-rw-r--r--src/backenddb/pg_authorize_reward.h64
-rw-r--r--src/backenddb/pg_helper.c121
-rw-r--r--src/backenddb/pg_helper.h49
-rw-r--r--src/backenddb/pg_increase_refund.c491
-rw-r--r--src/backenddb/pg_increase_refund.h56
-rw-r--r--src/backenddb/pg_insert_deposit.c41
-rw-r--r--src/backenddb/pg_insert_deposit_to_transfer.c64
-rw-r--r--src/backenddb/pg_insert_deposit_to_transfer.h44
-rw-r--r--src/backenddb/pg_insert_instance.c15
-rw-r--r--src/backenddb/pg_insert_pickup.c162
-rw-r--r--src/backenddb/pg_insert_pickup.h57
-rw-r--r--src/backenddb/pg_insert_product.c9
-rw-r--r--src/backenddb/pg_insert_reserve.c126
-rw-r--r--src/backenddb/pg_insert_reserve.h56
-rw-r--r--src/backenddb/pg_insert_transfer.c72
-rw-r--r--src/backenddb/pg_insert_transfer.h52
-rw-r--r--src/backenddb/pg_insert_transfer_details.c290
-rw-r--r--src/backenddb/pg_insert_transfer_details.h51
-rw-r--r--src/backenddb/pg_lookup_deposits.c12
-rw-r--r--src/backenddb/pg_lookup_instances.c12
-rw-r--r--src/backenddb/pg_lookup_product.c3
-rw-r--r--src/backenddb/pg_lookup_refunds.c3
-rw-r--r--src/backenddb/pg_lookup_reserves.c12
-rw-r--r--src/backenddb/pg_lookup_transfers.c24
-rw-r--r--src/backenddb/pg_refund_coin.c6
-rw-r--r--src/backenddb/pg_select_open_transfers.c3
-rw-r--r--src/backenddb/pg_set_transfer_status_to_confirmed.c6
-rw-r--r--src/backenddb/pg_store_wire_fee_by_exchange.c76
-rw-r--r--src/backenddb/pg_store_wire_fee_by_exchange.h52
-rw-r--r--src/backenddb/pg_update_instance.c27
-rw-r--r--src/backenddb/pg_update_product.c23
-rw-r--r--src/backenddb/plugin_merchantdb_postgres.c1916
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/16891837042
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/16897882041
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/16891837041
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/16897882041
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/16891837041
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/16897882042
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689183704bin32 -> 0 bytes
-rw-r--r--src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/16897882041
46 files changed, 2616 insertions, 2031 deletions
diff --git a/contrib/wallet-core b/contrib/wallet-core
-Subproject 4664263ea1a430ea70e6be34ed0c7d9946aa4e2
+Subproject 7079bce1ad2640e44561f56b46d5f00758df8e5
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index 240b1cc1..3d7d7827 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -56,6 +56,15 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
pg_select_wirewatch_accounts.h pg_select_wirewatch_accounts.c \
pg_insert_account.h pg_insert_account.c \
pg_update_account.h pg_update_account.c \
+ pg_insert_deposit_to_transfer.h pg_insert_deposit_to_transfer.c \
+ pg_increase_refund.h pg_increase_refund.c \
+ pg_insert_transfer.h pg_insert_transfer.c \
+ pg_insert_transfer_details.h pg_insert_transfer_details.c \
+ pg_store_wire_fee_by_exchange.h pg_store_wire_fee_by_exchange.c \
+ pg_insert_reserve.h pg_insert_reserve.c \
+ pg_activate_reserve.h pg_activate_reserve.c \
+ pg_authorize_reward.h pg_authorize_reward.c \
+ pg_insert_pickup.h pg_insert_pickup.c \
pg_select_open_transfers.h pg_select_open_transfers.c \
pg_lookup_instances.h pg_lookup_instances.c \
pg_lookup_transfers.h pg_lookup_transfers.c \
@@ -105,7 +114,8 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
pg_lookup_order_status.h pg_lookup_order_status.c \
pg_lookup_order_status_by_serial.h pg_lookup_order_status_by_serial.c \
pg_lookup_payment_status.h pg_lookup_payment_status.c \
- plugin_merchantdb_postgres.c pg_helper.h
+ plugin_merchantdb_postgres.c \
+ pg_helper.h pg_helper.c
libtaler_plugin_merchantdb_postgres_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_merchantdb_postgres_la_LDFLAGS = \
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 7bd8d737..e959a071 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -30,6 +30,14 @@ COMMENT ON SCHEMA merchant IS 'taler-merchant data';
SET search_path TO merchant;
+CREATE TYPE taler_amount
+ AS
+ (val INT8
+ ,frac INT4
+ );
+COMMENT ON TYPE taler_amount
+ IS 'Stores an amount, fraction is in units of 1/100000000 of the base value';
+
---------------- Exchange information ---------------------------
CREATE TABLE IF NOT EXISTS merchant_exchange_wire_fees
@@ -38,10 +46,8 @@ CREATE TABLE IF NOT EXISTS merchant_exchange_wire_fees
,h_wire_method BYTEA NOT NULL CHECK (LENGTH(h_wire_method)=64)
,start_date INT8 NOT NULL
,end_date INT8 NOT NULL
- ,wire_fee_val INT8 NOT NULL
- ,wire_fee_frac INT4 NOT NULL
- ,closing_fee_val INT8 NOT NULL
- ,closing_fee_frac INT4 NOT NULL
+ ,wire_fee taler_amount NOT NULL
+ ,closing_fee taler_amount NOT NULL
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
,UNIQUE (master_pub,h_wire_method,start_date)
);
@@ -80,10 +86,8 @@ CREATE TABLE IF NOT EXISTS merchant_instances
,logo BYTEA
,address BYTEA NOT NULL
,jurisdiction BYTEA NOT NULL
- ,default_max_deposit_fee_val INT8 NOT NULL
- ,default_max_deposit_fee_frac INT4 NOT NULL
- ,default_max_wire_fee_val INT8 NOT NULL
- ,default_max_wire_fee_frac INT4 NOT NULL
+ ,default_max_deposit_fee taler_amount NOT NULL
+ ,default_max_wire_fee taler_amount NOT NULL
,default_wire_fee_amortization INT4 NOT NULL
,default_wire_transfer_delay INT8 NOT NULL
,default_pay_delay INT8 NOT NULL
@@ -166,8 +170,7 @@ CREATE TABLE IF NOT EXISTS merchant_inventory
,unit VARCHAR NOT NULL
,image BYTEA NOT NULL
,taxes BYTEA NOT NULL
- ,price_val INT8 NOT NULL
- ,price_frac INT4 NOT NULL
+ ,price taler_amount NOT NULL
,total_stock BIGINT NOT NULL
,total_sold BIGINT NOT NULL DEFAULT 0
,total_lost BIGINT NOT NULL DEFAULT 0
@@ -188,7 +191,7 @@ COMMENT ON COLUMN merchant_inventory.image
IS 'NOT NULL, but can be 0 bytes; must contain an ImageDataUrl';
COMMENT ON COLUMN merchant_inventory.taxes
IS 'JSON array containing taxes the merchant pays, must be JSON, but can be just "[]"';
-COMMENT ON COLUMN merchant_inventory.price_val
+COMMENT ON COLUMN merchant_inventory.price
IS 'Current price of one unit of the product';
COMMENT ON COLUMN merchant_inventory.total_stock
IS 'A value of -1 is used for unlimited (electronic good), may never be lowered';
@@ -358,14 +361,10 @@ CREATE TABLE IF NOT EXISTS merchant_deposits
,deposit_timestamp INT8 NOT NULL
,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)
,exchange_url VARCHAR NOT NULL
- ,amount_with_fee_val INT8 NOT NULL
- ,amount_with_fee_frac INT4 NOT NULL
- ,deposit_fee_val INT8 NOT NULL
- ,deposit_fee_frac INT4 NOT NULL
- ,refund_fee_val INT8 NOT NULL
- ,refund_fee_frac INT4 NOT NULL
- ,wire_fee_val INT8 NOT NULL
- ,wire_fee_frac INT4 NOT NULL
+ ,amount_with_fee taler_amount NOT NULL
+ ,deposit_fee taler_amount NOT NULL
+ ,refund_fee taler_amount NOT NULL
+ ,wire_fee taler_amount NOT NULL
,signkey_serial BIGINT NOT NULL
REFERENCES merchant_exchange_signing_keys (signkey_serial) ON DELETE CASCADE
,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64)
@@ -381,7 +380,7 @@ COMMENT ON COLUMN merchant_deposits.deposit_timestamp
IS 'Time when the exchange generated the deposit confirmation';
COMMENT ON COLUMN merchant_deposits.exchange_sig
IS 'Signature of the exchange over the deposit confirmation';
-COMMENT ON COLUMN merchant_deposits.wire_fee_val
+COMMENT ON COLUMN merchant_deposits.wire_fee
IS 'We MAY want to see if we should try to get this via merchant_exchange_wire_fees (not sure, may be too complicated with the date range, etc.)';
CREATE TABLE IF NOT EXISTS merchant_refunds
@@ -392,8 +391,7 @@ CREATE TABLE IF NOT EXISTS merchant_refunds
,refund_timestamp INT8 NOT NULL
,coin_pub BYTEA NOT NULL
,reason VARCHAR NOT NULL
- ,refund_amount_val INT8 NOT NULL
- ,refund_amount_frac INT4 NOT NULL
+ ,refund_amount taler_amount NOT NULL
,UNIQUE (order_serial, coin_pub, rtransaction_id)
);
COMMENT ON TABLE merchant_deposits
@@ -422,8 +420,7 @@ CREATE TABLE IF NOT EXISTS merchant_transfers
(credit_serial INT8 GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
,exchange_url VARCHAR NOT NULL
,wtid BYTEA CHECK (LENGTH(wtid)=32)
- ,credit_amount_val INT8 NOT NULL
- ,credit_amount_frac INT4 NOT NULL
+ ,credit_amount taler_amount NOT NULL
,account_serial INT8 NOT NULL
REFERENCES merchant_accounts (account_serial) ON DELETE CASCADE
,ready_time INT8 NOT NULL DEFAULT (0)
@@ -439,7 +436,7 @@ COMMENT ON COLUMN merchant_transfers.verified
IS 'true once we got an acceptable response from the exchange for this transfer';
COMMENT ON COLUMN merchant_transfers.confirmed
IS 'true once the merchant confirmed that this transfer was received';
-COMMENT ON COLUMN merchant_transfers.credit_amount_val
+COMMENT ON COLUMN merchant_transfers.credit_amount
IS 'actual value of the (aggregated) wire transfer, excluding the wire fee, according to the merchant';
COMMENT ON COLUMN merchant_transfers.failed
IS 'set to true on permanent verification failures';
@@ -459,10 +456,8 @@ CREATE TABLE IF NOT EXISTS merchant_transfer_signatures
REFERENCES merchant_transfers (credit_serial) ON DELETE CASCADE
,signkey_serial BIGINT NOT NULL
REFERENCES merchant_exchange_signing_keys (signkey_serial) ON DELETE CASCADE
- ,wire_fee_val INT8 NOT NULL
- ,wire_fee_frac INT4 NOT NULL
- ,credit_amount_val INT8 NOT NULL
- ,credit_amount_frac INT4 NOT NULL
+ ,wire_fee taler_amount NOT NULL
+ ,credit_amount taler_amount NOT NULL
,execution_time INT8 NOT NULL
,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64)
);
@@ -470,7 +465,7 @@ COMMENT ON TABLE merchant_transfer_signatures
IS 'table represents the main information returned from the /transfer request to the exchange.';
COMMENT ON COLUMN merchant_transfer_signatures.execution_time
IS 'Execution time as claimed by the exchange, roughly matches time seen by merchant';
-COMMENT ON COLUMN merchant_transfer_signatures.credit_amount_val
+COMMENT ON COLUMN merchant_transfer_signatures.credit_amount
IS 'actual value of the (aggregated) wire transfer, excluding the wire fee, according to the exchange';
@@ -480,26 +475,23 @@ CREATE TABLE IF NOT EXISTS merchant_transfer_to_coin
,credit_serial BIGINT NOT NULL
REFERENCES merchant_transfers (credit_serial) ON DELETE CASCADE
,offset_in_exchange_list INT8 NOT NULL
- ,exchange_deposit_value_val INT8 NOT NULL
- ,exchange_deposit_value_frac INT4 NOT NULL
- ,exchange_deposit_fee_val INT8 NOT NULL
- ,exchange_deposit_fee_frac INT4 NOT NULL
+ ,exchange_deposit_value taler_amount NOT NULL
+ ,exchange_deposit_fee taler_amount NOT NULL
);
CREATE INDEX IF NOT EXISTS merchant_transfers_by_credit
ON merchant_transfer_to_coin
(credit_serial);
COMMENT ON TABLE merchant_transfer_to_coin
IS 'Mapping of (credit) transfers to (deposited) coins';
-COMMENT ON COLUMN merchant_transfer_to_coin.exchange_deposit_value_val
+COMMENT ON COLUMN merchant_transfer_to_coin.exchange_deposit_value
IS 'Deposit value as claimed by the exchange, should match our values in merchant_deposits minus refunds';
-COMMENT ON COLUMN merchant_transfer_to_coin.exchange_deposit_fee_val
+COMMENT ON COLUMN merchant_transfer_to_coin.exchange_deposit_fee
IS 'Deposit value as claimed by the exchange, should match our values in merchant_deposits';
CREATE TABLE IF NOT EXISTS merchant_deposit_to_transfer
(deposit_serial BIGINT NOT NULL
REFERENCES merchant_deposits (deposit_serial) ON DELETE CASCADE
- ,coin_contribution_value_val INT8 NOT NULL
- ,coin_contribution_value_frac INT4 NOT NULL
+ ,coin_contribution_value taler_amount NOT NULL
,credit_serial BIGINT NOT NULL
REFERENCES merchant_transfers (credit_serial)
,execution_time INT8 NOT NULL
@@ -523,26 +515,22 @@ CREATE TABLE IF NOT EXISTS merchant_reward_reserves
REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
,creation_time INT8 NOT NULL
,expiration INT8 NOT NULL
- ,merchant_initial_balance_val INT8 NOT NULL
- ,merchant_initial_balance_frac INT4 NOT NULL
- ,exchange_initial_balance_val INT8 NOT NULL DEFAULT 0
- ,exchange_initial_balance_frac INT4 NOT NULL DEFAULT 0
- ,rewards_committed_val INT8 NOT NULL DEFAULT 0
- ,rewards_committed_frac INT4 NOT NULL DEFAULT 0
- ,rewards_picked_up_val INT8 NOT NULL DEFAULT 0
- ,rewards_picked_up_frac INT4 NOT NULL DEFAULT 0
+ ,merchant_initial_balance taler_amount NOT NULL
+ ,exchange_initial_balance taler_amount NOT NULL DEFAULT (0,0)
+ ,rewards_committed taler_amount NOT NULL DEFAULT (0,0)
+ ,rewards_picked_up taler_amount NOT NULL DEFAULT (0,0)
);
COMMENT ON TABLE merchant_reward_reserves
IS 'balances of the reserves available for rewards';
COMMENT ON COLUMN merchant_reward_reserves.expiration
IS 'FIXME: EXCHANGE API needs to tell us when reserves close if we are to compute this';
-COMMENT ON COLUMN merchant_reward_reserves.merchant_initial_balance_val
+COMMENT ON COLUMN merchant_reward_reserves.merchant_initial_balance
IS 'Set to the initial balance the merchant told us when creating the reserve';
-COMMENT ON COLUMN merchant_reward_reserves.exchange_initial_balance_val
+COMMENT ON COLUMN merchant_reward_reserves.exchange_initial_balance
IS 'Set to the initial balance the exchange told us when we queried the reserve status';
-COMMENT ON COLUMN merchant_reward_reserves.rewards_committed_val
+COMMENT ON COLUMN merchant_reward_reserves.rewards_committed
IS 'Amount of outstanding approved rewards that have not been picked up';
-COMMENT ON COLUMN merchant_reward_reserves.rewards_picked_up_val
+COMMENT ON COLUMN merchant_reward_reserves.rewards_picked_up
IS 'Total amount rewards that have been picked up from this reserve';
CREATE INDEX IF NOT EXISTS merchant_reward_reserves_by_reserve_pub_and_merchant_serial
@@ -553,7 +541,7 @@ CREATE INDEX IF NOT EXISTS merchant_reward_reserves_by_merchant_serial_and_creat
(merchant_serial,creation_time);
CREATE INDEX IF NOT EXISTS merchant_reward_reserves_by_exchange_balance
ON merchant_reward_reserves
- (exchange_initial_balance_val,exchange_initial_balance_frac);
+ (exchange_initial_balance);
@@ -578,10 +566,8 @@ CREATE TABLE IF NOT EXISTS merchant_rewards
,justification VARCHAR NOT NULL
,next_url VARCHAR NOT NULL
,expiration INT8 NOT NULL
- ,amount_val INT8 NOT NULL
- ,amount_frac INT4 NOT NULL
- ,picked_up_val INT8 NOT NULL DEFAULT 0
- ,picked_up_frac INT4 NOT NULL DEFAULT 0
+ ,amount taler_amount NOT NULL
+ ,picked_up taler_amount NOT NULL DEFAULT (0, 0)
,was_picked_up BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS merchant_rewards_by_pickup_and_expiration
@@ -589,9 +575,9 @@ CREATE INDEX IF NOT EXISTS merchant_rewards_by_pickup_and_expiration
(was_picked_up,expiration);
COMMENT ON TABLE merchant_rewards
IS 'rewards that have been authorized';
-COMMENT ON COLUMN merchant_rewards.amount_val
+COMMENT ON COLUMN merchant_rewards.amount
IS 'Overall reward amount';
-COMMENT ON COLUMN merchant_rewards.picked_up_val
+COMMENT ON COLUMN merchant_rewards.picked_up
IS 'Reward amount left to be picked up';
COMMENT ON COLUMN merchant_rewards.reserve_serial
IS 'Reserve from which this reward is funded';
@@ -603,12 +589,11 @@ CREATE TABLE IF NOT EXISTS merchant_reward_pickups
,reward_serial BIGINT NOT NULL
REFERENCES merchant_rewards (reward_serial) ON DELETE CASCADE
,pickup_id BYTEA NOT NULL UNIQUE CHECK (LENGTH(pickup_id)=64)
- ,amount_val INT8 NOT NULL
- ,amount_frac INT4 NOT NULL
+ ,amount taler_amount NOT NULL
);
COMMENT ON TABLE merchant_reward_pickups
IS 'rewards that have been picked up';
-COMMENT ON COLUMN merchant_rewards.amount_val
+COMMENT ON COLUMN merchant_rewards.amount
IS 'total transaction cost for all coins including withdraw fees';
CREATE TABLE IF NOT EXISTS merchant_reward_pickup_signatures
diff --git a/src/backenddb/pg_activate_reserve.c b/src/backenddb/pg_activate_reserve.c
new file mode 100644
index 00000000..14a945d5
--- /dev/null
+++ b/src/backenddb/pg_activate_reserve.c
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_activate_reserve.c
+ * @brief Implementation of the activate_reserve function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_activate_reserve.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_activate_reserve (void *cls,
+ const char *instance_id,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *initial_exchange_balance)
+{
+ 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),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ initial_exchange_balance),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "activate_reserve",
+ "UPDATE merchant_reward_reserves SET"
+ " exchange_initial_balance=$3"
+ " WHERE reserve_pub=$2"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1)");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "activate_reserve",
+ params);
+}
diff --git a/src/backenddb/pg_activate_reserve.h b/src/backenddb/pg_activate_reserve.h
new file mode 100644
index 00000000..3b7c8a3d
--- /dev/null
+++ b/src/backenddb/pg_activate_reserve.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_activate_reserve.h
+ * @brief implementation of the activate_reserve function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_ACTIVATE_RESERVE_H
+#define PG_ACTIVATE_RESERVE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Confirms @a credit as the amount the exchange claims to have received and
+ * thus really 'activates' the reserve. This has to happen before rewards can
+ * be authorized.
+ *
+ * @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 topped up or created
+ * @param initial_exchange_balance how much money was be added to the reserve
+ * according to the exchange
+ * @return transaction status, usually
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_activate_reserve (void *cls,
+ const char *instance_id,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *initial_exchange_balance);
+
+#endif
diff --git a/src/backenddb/pg_authorize_reward.c b/src/backenddb/pg_authorize_reward.c
new file mode 100644
index 00000000..771796e3
--- /dev/null
+++ b/src/backenddb/pg_authorize_reward.c
@@ -0,0 +1,422 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_authorize_reward.c
+ * @brief Implementation of the authorize_reward function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_authorize_reward.h"
+#include "pg_helper.h"
+
+
+/**
+ * How often do we re-try if we run into a DB serialization error?
+ */
+#define MAX_RETRIES 3
+
+
+/**
+ * Closure for #lookup_reserve_for_reward_cb().
+ */
+struct LookupReserveForRewardContext
+{
+ /**
+ * Postgres context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Public key of the reserve we found.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * How much money must be left in the reserve.
+ */
+ struct TALER_Amount required_amount;
+
+ /**
+ * Set to the expiration time of the reserve we found.
+ * #GNUNET_TIME_UNIT_FOREVER_ABS if we found none.
+ */
+ struct GNUNET_TIME_Timestamp expiration;
+
+ /**
+ * Error status.
+ */
+ enum TALER_ErrorCode ec;
+
+ /**
+ * Did we find a good reserve?
+ */
+ bool ok;
+};
+
+
+/**
+ * How long must a reserve be at least still valid before we use
+ * it for a reward?
+ */
+#define MIN_EXPIRATION GNUNET_TIME_UNIT_HOURS
+
+
+/**
+ * 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 LookupReserveForRewardContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_reserve_for_reward_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct LookupReserveForRewardContext *lac = cls;
+ struct PostgresClosure *pg = lac->pg;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_Amount committed_amount;
+ struct TALER_Amount remaining;
+ struct TALER_Amount initial_balance;
+ struct GNUNET_TIME_Timestamp expiration;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+ &reserve_pub),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
+ &initial_balance),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_committed",
+ &committed_amount),
+ GNUNET_PQ_result_spec_timestamp ("expiration",
+ &expiration),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ lac->ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
+ return;
+ }
+ if (0 >
+ TALER_amount_subtract (&remaining,
+ &initial_balance,
+ &committed_amount))
+ {
+ GNUNET_break (0);
+ continue;
+ }
+ if (0 >
+ TALER_amount_cmp (&remaining,
+ &lac->required_amount))
+ {
+ /* insufficient balance */
+ if (lac->ok)
+ continue; /* got another reserve */
+ lac->ec =
+ TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS;
+ continue;
+ }
+ if ( (! GNUNET_TIME_absolute_is_never (lac->expiration.abs_time)) &&
+ GNUNET_TIME_timestamp_cmp (expiration,
+ >,
+ lac->expiration) &&
+ GNUNET_TIME_relative_cmp (
+ GNUNET_TIME_absolute_get_remaining (lac->expiration.abs_time),
+ >,
+ MIN_EXPIRATION) )
+ {
+ /* reserve expired */
+ if (lac->ok)
+ continue; /* got another reserve */
+ lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_EXPIRED;
+ continue;
+ }
+ lac->ok = true;
+ lac->ec = TALER_EC_NONE;
+ lac->expiration = expiration;
+ lac->reserve_pub = reserve_pub;
+ }
+}
+
+
+enum TALER_ErrorCode
+TMH_PG_authorize_reward (void *cls,
+ const char *instance_id,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *amount,
+ const char *justification,
+ const char *next_url,
+ struct TALER_RewardIdentifierP *reward_id,
+ struct GNUNET_TIME_Timestamp *expiration)
+{
+ struct PostgresClosure *pg = cls;
+ unsigned int retries = 0;
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_Amount rewards_committed;
+ struct TALER_Amount exchange_initial_balance;
+ const struct TALER_ReservePublicKeyP *reserve_pubp;
+ struct LookupReserveForRewardContext lac = {
+ .pg = pg,
+ .required_amount = *amount,
+ .expiration = GNUNET_TIME_UNIT_FOREVER_TS
+ };
+
+ check_connection (pg);
+ PREPARE (pg,
+ "lookup_reserve_for_reward",
+ "SELECT"
+ " reserve_pub"
+ ",expiration"
+ ",exchange_initial_balance"
+ ",rewards_committed"
+ " FROM merchant_reward_reserves"
+ " WHERE"
+ " merchant_serial ="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1)");
+ PREPARE (pg,
+ "lookup_reserve_status",
+ "SELECT"
+ " expiration"
+ ",exchange_initial_balance"
+ ",rewards_committed"
+ " FROM merchant_reward_reserves"
+ " WHERE reserve_pub = $2"
+ " AND merchant_serial ="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1)");
+ PREPARE (pg,
+ "update_reserve_rewards_committed",
+ "UPDATE merchant_reward_reserves SET"
+ " rewards_committed=$3"
+ " WHERE reserve_pub=$2"
+ " AND merchant_serial ="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1)");
+ PREPARE (pg,
+ "insert_reward",
+ "INSERT INTO merchant_rewards"
+ "(reserve_serial"
+ ",reward_id"
+ ",justification"
+ ",next_url"
+ ",expiration"
+ ",amount"
+ ") "
+ "SELECT"
+ " reserve_serial, $3, $4, $5, $6, $7"
+ " FROM merchant_reward_reserves"
+ " WHERE reserve_pub=$2"
+ " AND merchant_serial = "
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1)");
+RETRY:
+ reserve_pubp = reserve_pub;
+ if (MAX_RETRIES < ++retries)
+ {
+ GNUNET_break (0);
+ return
+ TALER_EC_GENERIC_DB_SOFT_FAILURE;
+ }
+ if (GNUNET_OK !=
+ TMH_PG_start (pg,
+ "authorize reward"))
+ {
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_DB_START_FAILED;
+ }
+ if (NULL == reserve_pubp)
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "lookup_reserve_for_reward",
+ params,
+ &lookup_reserve_for_reward_cb,
+ &lac);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ TMH_PG_rollback (pg);
+ goto RETRY;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ TMH_PG_rollback (pg);
+ return TALER_EC_GENERIC_DB_FETCH_FAILED;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ TMH_PG_rollback (pg);
+ return TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_NOT_FOUND;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ default:
+ break;
+ }
+ if (TALER_EC_NONE != lac.ec)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Enabling reward reserved failed with status %d\n",
+ lac.ec);
+ TMH_PG_rollback (pg);
+ return lac.ec;
+ }
+ GNUNET_assert (lac.ok);
+ reserve_pubp = &lac.reserve_pub;
+ }
+
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_timestamp ("expiration",
+ expiration),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_committed",
+ &rewards_committed),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
+ &exchange_initial_balance),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_reserve_status",
+ params,
+ rs);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ TMH_PG_rollback (pg);
+ goto RETRY;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ TMH_PG_rollback (pg);
+ return TALER_EC_GENERIC_DB_FETCH_FAILED;
+ }
+ if (0 == qs)
+ {
+ TMH_PG_rollback (pg);
+ return TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_NOT_FOUND;
+ }
+ }
+ {
+ struct TALER_Amount remaining;
+
+ if (0 >
+ TALER_amount_subtract (&remaining,
+ &exchange_initial_balance,
+ &rewards_committed))
+ {
+ GNUNET_break (0);
+ TMH_PG_rollback (pg);
+ return TALER_EC_GENERIC_DB_INVARIANT_FAILURE;
+ }
+ if (0 >
+ TALER_amount_cmp (&remaining,
+ amount))
+ {
+ TMH_PG_rollback (pg);
+ return TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS;
+ }
+ }
+ GNUNET_assert (0 <=
+ TALER_amount_add (&rewards_committed,
+ &rewards_committed,
+ amount));
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &rewards_committed),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_reserve_rewards_committed",
+ params);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ TMH_PG_rollback (pg);
+ goto RETRY;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ TMH_PG_rollback (pg);
+ return TALER_EC_GENERIC_DB_STORE_FAILED;
+ }
+ }
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ reward_id,
+ sizeof (*reward_id));
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
+ GNUNET_PQ_query_param_auto_from_type (reward_id),
+ GNUNET_PQ_query_param_string (justification),
+ GNUNET_PQ_query_param_string (next_url),
+ GNUNET_PQ_query_param_timestamp (expiration),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ amount),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_reward",
+ params);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ TMH_PG_rollback (pg);
+ goto RETRY;
+ }
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ TMH_PG_rollback (pg);
+ return TALER_EC_GENERIC_DB_STORE_FAILED;
+ }
+ }
+ qs = TMH_PG_commit (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ TMH_PG_rollback (pg);
+ return TALER_EC_GENERIC_DB_COMMIT_FAILED;
+ }
+ return TALER_EC_NONE;
+}
diff --git a/src/backenddb/pg_authorize_reward.h b/src/backenddb/pg_authorize_reward.h
new file mode 100644
index 00000000..4ef56bf3
--- /dev/null
+++ b/src/backenddb/pg_authorize_reward.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_authorize_reward.h
+ * @brief implementation of the authorize_reward function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_AUTHORIZE_REWARD_H
+#define PG_AUTHORIZE_REWARD_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Authorize a reward over @a amount from reserve @a reserve_pub. Remember
+ * the authorization under @a reward_id for later, together with the
+ * @a justification.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param instance_id which instance should generate the reward
+ * @param reserve_pub which reserve is debited, NULL to pick one in the DB
+ * @param amount how high is the reward (with fees)
+ * @param justification why was the reward approved
+ * @param next_url where to send the URL post reward pickup
+ * @param[out] reward_id set to the unique ID for the reward
+ * @param[out] expiration set to when the reward expires
+ * @return transaction status,
+ * #TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
+ * #TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_NOT_FOUND if the reserve is not known
+ * #TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
+ * #TALER_EC_GENERIC_DB_START_FAILED on hard DB errors
+ * #TALER_EC_GENERIC_DB_FETCH_FAILED on hard DB errors
+ * #TALER_EC_GENERIC_DB_STORE_FAILED on hard DB errors
+ * #TALER_EC_GENERIC_DB_INVARIANT_FAILURE on hard DB errors
+ * #TALER_EC_GENERIC_DB_SOFT_FAILURE soft DB errors (client should retry)
+ * #TALER_EC_NONE upon success
+ */
+enum TALER_ErrorCode
+TMH_PG_authorize_reward (void *cls,
+ const char *instance_id,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *amount,
+ const char *justification,
+ const char *next_url,
+ struct TALER_RewardIdentifierP *reward_id,
+ struct GNUNET_TIME_Timestamp *expiration);
+
+
+#endif
diff --git a/src/backenddb/pg_helper.c b/src/backenddb/pg_helper.c
new file mode 100644
index 00000000..2ac047b1
--- /dev/null
+++ b/src/backenddb/pg_helper.c
@@ -0,0 +1,121 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_helper.c
+ * @brief shared internal definitions for postgres DB plugin
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "pg_helper.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_pq_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_pq_lib.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_mhd_lib.h>
+
+
+enum GNUNET_GenericReturnValue
+TMH_PG_start (void *cls,
+ const char *name)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
+ check_connection (pg);
+ postgres_preflight (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting merchant DB transaction `%s'\n",
+ name);
+ if (GNUNET_OK !=
+ GNUNET_PQ_exec_statements (pg->conn,
+ es))
+ {
+ TALER_LOG_ERROR ("Failed to start transaction\n");
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ pg->transaction_name = name;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TMH_PG_start_read_committed (void *cls,
+ const char *name)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
+ check_connection (pg);
+ postgres_preflight (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Starting merchant DB transaction %s (READ COMMITTED)\n",
+ name);
+ if (GNUNET_OK !=
+ GNUNET_PQ_exec_statements (pg->conn,
+ es))
+ {
+ TALER_LOG_ERROR ("Failed to start transaction\n");
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ pg->transaction_name = name;
+ return GNUNET_OK;
+}
+
+
+void
+TMH_PG_rollback (void *cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_ExecuteStatement es[] = {
+ GNUNET_PQ_make_execute ("ROLLBACK"),
+ GNUNET_PQ_EXECUTE_STATEMENT_END
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Rolling back merchant DB transaction `%s'\n",
+ pg->transaction_name);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_PQ_exec_statements (pg->conn,
+ es));
+ pg->transaction_name = NULL;
+}
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_commit (void *cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_end
+ };
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Committing merchant DB transaction %s\n",
+ pg->transaction_name);
+ pg->transaction_name = NULL;
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "end_transaction",
+ params);
+}
diff --git a/src/backenddb/pg_helper.h b/src/backenddb/pg_helper.h
index bb8e7255..83fede8f 100644
--- a/src/backenddb/pg_helper.h
+++ b/src/backenddb/pg_helper.h
@@ -102,7 +102,8 @@ struct PostgresClosure
* @param field name of the database field to fetch amount from
* @param[out] amountp pointer to amount to set
*/
-#define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) TALER_PQ_result_spec_amount ( \
+#define TALER_PQ_RESULT_SPEC_AMOUNT(field, \
+ amountp) TALER_PQ_result_spec_amount_tuple ( \
field,pg->currency,amountp)
@@ -114,7 +115,8 @@ struct PostgresClosure
* @param[out] amountp pointer to amount to set
*/
#define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, \
- amountp) TALER_PQ_result_spec_amount_nbo ( \
+ amountp) \
+ TALER_PQ_result_spec_amount_tuple_nbo ( \
field,pg->currency,amountp)
@@ -139,5 +141,48 @@ check_connection (struct PostgresClosure *pg);
void
postgres_preflight (void *cls);
+/**
+ * Start a transaction.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param name unique name identifying the transaction (for debugging),
+ * must point to a constant
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TMH_PG_start (void *cls,
+ const char *name);
+
+
+/**
+ * Start a transaction in 'read committed' mode.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param name unique name identifying the transaction (for debugging),
+ * must point to a constant
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TMH_PG_start_read_committed (void *cls,
+ const char *name);
+
+/**
+ * Roll back the current transaction of a database connection.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ */
+void
+TMH_PG_rollback (void *cls);
+
+
+/**
+ * Commit the current transaction of a database connection.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_commit (void *cls);
+
#endif
diff --git a/src/backenddb/pg_increase_refund.c b/src/backenddb/pg_increase_refund.c
new file mode 100644
index 00000000..3b0842d6
--- /dev/null
+++ b/src/backenddb/pg_increase_refund.c
@@ -0,0 +1,491 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_increase_refund.c
+ * @brief Implementation of the increase_refund function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_increase_refund.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #process_refund_cb().
+ */
+struct FindRefundContext
+{
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Updated to reflect total amount refunded so far.
+ */
+ struct TALER_Amount refunded_amount;
+
+ /**
+ * Set to the largest refund transaction ID encountered.
+ */
+ uint64_t max_rtransaction_id;
+
+ /**
+ * Set to true on hard errors.
+ */
+ bool err;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure, our `struct FindRefundContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+process_refund_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct FindRefundContext *ictx = cls;
+ struct PostgresClosure *pg = ictx->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ /* Sum up existing refunds */
+ struct TALER_Amount acc;
+ uint64_t rtransaction_id;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
+ &acc),
+ GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+ &rtransaction_id),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ictx->err = true;
+ return;
+ }
+ if (0 >
+ TALER_amount_add (&ictx->refunded_amount,
+ &ictx->refunded_amount,
+ &acc))
+ {
+ GNUNET_break (0);
+ ictx->err = true;
+ return;
+ }
+ ictx->max_rtransaction_id = GNUNET_MAX (ictx->max_rtransaction_id,
+ rtransaction_id);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Found refund of %s\n",
+ TALER_amount2s (&acc));
+ }
+}
+
+
+/**
+ * Closure for #process_deposits_for_refund_cb().
+ */
+struct InsertRefundContext
+{
+ /**
+ * Used to provide a connection to the db
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Amount to which increase the refund for this contract
+ */
+ const struct TALER_Amount *refund;
+
+ /**
+ * Human-readable reason behind this refund
+ */
+ const char *reason;
+
+ /**
+ * Transaction status code.
+ */
+ enum TALER_MERCHANTDB_RefundStatus rs;
+};
+
+
+/**
+ * Data extracted per coin.
+ */
+struct RefundCoinData
+{
+
+ /**
+ * Public key of a coin.
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ * Amount deposited for this coin.
+ */
+ struct TALER_Amount deposited_with_fee;
+
+ /**
+ * Amount refunded already for this coin.
+ */
+ struct TALER_Amount refund_amount;
+
+ /**
+ * Order serial (actually not really per-coin).
+ */
+ uint64_t order_serial;
+
+ /**
+ * Maximum rtransaction_id for this coin so far.
+ */
+ uint64_t max_rtransaction_id;
+
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure, our `struct InsertRefundContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+process_deposits_for_refund_cb (
+ void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct InsertRefundContext *ctx = cls;
+ struct PostgresClosure *pg = ctx->pg;
+ struct TALER_Amount current_refund;
+ struct RefundCoinData rcd[GNUNET_NZL (num_results)];
+ struct GNUNET_TIME_Timestamp now;
+
+ now = GNUNET_TIME_timestamp_get ();
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (ctx->refund->currency,
+ &current_refund));
+ memset (rcd, 0, sizeof (rcd));
+ /* Pass 1: Collect amount of existing refunds into current_refund.
+ * Also store existing refunded amount for each deposit in deposit_refund. */
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+ &rcd[i].coin_pub),
+ GNUNET_PQ_result_spec_uint64 ("order_serial",
+ &rcd[i].order_serial),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+ &rcd[i].deposited_with_fee),
+ GNUNET_PQ_result_spec_end
+ };
+ struct FindRefundContext ictx = {
+ .pg = pg
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ }
+
+ {
+ enum GNUNET_DB_QueryStatus ires;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
+ GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
+ GNUNET_PQ_query_param_end
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (ctx->refund->currency,
+ &ictx.refunded_amount));
+ ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
+ "find_refunds_by_coin",
+ params,
+ &process_refund_cb,
+ &ictx);
+ if ( (ictx.err) ||
+ (GNUNET_DB_STATUS_HARD_ERROR == ires) )
+ {
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ }
+ if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
+ {
+ ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
+ return;
+ }
+ }
+ if (0 >
+ TALER_amount_add (&current_refund,
+ &current_refund,
+ &ictx.refunded_amount))
+ {
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ }
+ rcd[i].refund_amount = ictx.refunded_amount;
+ rcd[i].max_rtransaction_id = ictx.max_rtransaction_id;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Existing refund for coin %s is %s\n",
+ TALER_B2S (&rcd[i].coin_pub),
+ TALER_amount2s (&ictx.refunded_amount));
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Total existing refund is %s\n",
+ TALER_amount2s (&current_refund));
+
+ /* stop immediately if we are 'done' === amount already
+ * refunded. */
+ if (0 >= TALER_amount_cmp (ctx->refund,
+ &current_refund))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Existing refund of %s at or above requested refund. Finished early.\n",
+ TALER_amount2s (&current_refund));
+ ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
+ return;
+ }
+
+ /* Phase 2: Try to increase current refund until it matches desired refund */
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ const struct TALER_Amount *increment;
+ struct TALER_Amount left;
+ struct TALER_Amount remaining_refund;
+
+ /* How much of the coin is left after the existing refunds? */
+ if (0 >
+ TALER_amount_subtract (&left,
+ &rcd[i].deposited_with_fee,
+ &rcd[i].refund_amount))
+ {
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ }
+
+ if ( (0 == left.value) &&
+ (0 == left.fraction) )
+ {
+ /* coin was fully refunded, move to next coin */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Coin %s fully refunded, moving to next coin\n",
+ TALER_B2S (&rcd[i].coin_pub));
+ continue;
+ }
+
+ rcd[i].max_rtransaction_id++;
+ /* How much of the refund is still to be paid back? */
+ if (0 >
+ TALER_amount_subtract (&remaining_refund,
+ ctx->refund,
+ &current_refund))
+ {
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ }
+
+ /* By how much will we increase the refund for this coin? */
+ if (0 >= TALER_amount_cmp (&remaining_refund,
+ &left))
+ {
+ /* remaining_refund <= left */
+ increment = &remaining_refund;
+ }
+ else
+ {
+ increment = &left;
+ }
+
+ if (0 >
+ TALER_amount_add (&current_refund,
+ &current_refund,
+ increment))
+ {
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ }
+
+ /* actually run the refund */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Coin %s deposit amount is %s\n",
+ TALER_B2S (&rcd[i].coin_pub),
+ TALER_amount2s (&rcd[i].deposited_with_fee));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Coin %s refund will be incremented by %s\n",
+ TALER_B2S (&rcd[i].coin_pub),
+ TALER_amount2s (increment));
+ {
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
+ GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already inc'ed */
+ GNUNET_PQ_query_param_timestamp (&now),
+ GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
+ GNUNET_PQ_query_param_string (ctx->reason),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ increment),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_refund",
+ params);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+ return;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
+ return;
+ default:
+ ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs;
+ break;
+ }
+ }
+
+ /* stop immediately if we are done */
+ if (0 == TALER_amount_cmp (ctx->refund,
+ &current_refund))
+ {
+ ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
+ return;
+ }
+ }
+
+ /**
+ * We end up here if not all of the refund has been covered.
+ * Although this should be checked as the business should never
+ * issue a refund bigger than the contract's actual price, we cannot
+ * rely upon the frontend being correct.
+ */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "The refund of %s is bigger than the order's value\n",
+ TALER_amount2s (ctx->refund));
+ ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH;
+}
+
+
+enum TALER_MERCHANTDB_RefundStatus
+TMH_PG_increase_refund (void *cls,
+ const char *instance_id,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ const char *reason)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_string (order_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct InsertRefundContext ctx = {
+ .pg = pg,
+ .refund = refund,
+ .reason = reason
+ };
+
+ PREPARE (pg,
+ "insert_refund",
+ "INSERT INTO merchant_refunds"
+ "(order_serial"
+ ",rtransaction_id"
+ ",refund_timestamp"
+ ",coin_pub"
+ ",reason"
+ ",refund_amount"
+ ") VALUES"
+ "($1, $2, $3, $4, $5, $6)");
+ PREPARE (pg,
+ "find_refunds_by_coin",
+ "SELECT"
+ " refund_amount"
+ ",rtransaction_id"
+ " FROM merchant_refunds"
+ " WHERE coin_pub=$1"
+ " AND order_serial=$2");
+ PREPARE (pg,
+ "find_deposits_for_refund",
+ "SELECT"
+ " coin_pub"
+ ",order_serial"
+ ",amount_with_fee"
+ " FROM merchant_deposits"
+ " WHERE order_serial="
+ " (SELECT order_serial"
+ " FROM merchant_contract_terms"
+ " WHERE order_id=$2"
+ " AND paid=TRUE"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1))");
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Asked to refund %s on order %s\n",
+ TALER_amount2s (refund),
+ order_id);
+ qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "find_deposits_for_refund",
+ params,
+ &process_deposits_for_refund_cb,
+ &ctx);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* never paid, means we clearly cannot refund anything */
+ return TALER_MERCHANTDB_RS_NO_SUCH_ORDER;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return TALER_MERCHANTDB_RS_SOFT_ERROR;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ return TALER_MERCHANTDB_RS_HARD_ERROR;
+ default:
+ /* Got one or more deposits */
+ return ctx.rs;
+ }
+}
diff --git a/src/backenddb/pg_increase_refund.h b/src/backenddb/pg_increase_refund.h
new file mode 100644
index 00000000..0fe8a470
--- /dev/null
+++ b/src/backenddb/pg_increase_refund.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_increase_refund.h
+ * @brief implementation of the increase_refund function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INCREASE_REFUND_H
+#define PG_INCREASE_REFUND_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Function called when some backoffice staff decides to award or
+ * increase the refund on an existing contract. This function
+ * MUST be called from within a transaction scope setup by the
+ * caller as it executes mulrewardle SQL statements.
+ *
+ * @param cls closure
+ * @param instance_id instance identifier
+ * @param order_id the order to increase the refund for
+ * @param refund maximum refund to return to the customer for this contract
+ * @param reason 0-terminated UTF-8 string giving the reason why the customer
+ * got a refund (free form, business-specific)
+ * @return transaction status
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we
+ * were originally paid and thus the transaction failed;
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
+ * regardless of whether it actually increased the refund beyond
+ * what was already refunded (idempotency!)
+ */
+enum TALER_MERCHANTDB_RefundStatus
+TMH_PG_increase_refund (void *cls,
+ const char *instance_id,
+ const char *order_id,
+ const struct TALER_Amount *refund,
+ const char *reason);
+
+
+#endif
diff --git a/src/backenddb/pg_insert_deposit.c b/src/backenddb/pg_insert_deposit.c
index f7f20d47..2c1d1dd4 100644
--- a/src/backenddb/pg_insert_deposit.c
+++ b/src/backenddb/pg_insert_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -16,6 +16,7 @@
/**
* @file backenddb/pg_insert_deposit.c
* @brief Implementation of the insert_deposit function for Postgres
+ * @author Christian Grothoff
* @author Iván Ávalos
*/
#include "platform.h"
@@ -25,6 +26,7 @@
#include "pg_insert_deposit.h"
#include "pg_helper.h"
+
enum GNUNET_DB_QueryStatus
TMH_PG_insert_deposit (
void *cls,
@@ -48,13 +50,17 @@ TMH_PG_insert_deposit (
GNUNET_PQ_query_param_timestamp (&deposit_timestamp), /* $3 */
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_string (exchange_url),
- TALER_PQ_query_param_amount (amount_with_fee), /* $6/$7 */
- TALER_PQ_query_param_amount (deposit_fee), /* $8, $9 */
- TALER_PQ_query_param_amount (refund_fee), /* $10, $11 */
- TALER_PQ_query_param_amount (wire_fee), /* $12, $13 */
- GNUNET_PQ_query_param_auto_from_type (h_wire), /* $14 */
- GNUNET_PQ_query_param_auto_from_type (exchange_sig), /* $15 */
- GNUNET_PQ_query_param_auto_from_type (exchange_pub), /* $16 */
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ amount_with_fee), /* $6 */
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ deposit_fee), /* $7 */
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ refund_fee), /* $8 */
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ wire_fee), /* $9 */
+ GNUNET_PQ_query_param_auto_from_type (h_wire), /* $10 */
+ GNUNET_PQ_query_param_auto_from_type (exchange_sig), /* $11 */
+ GNUNET_PQ_query_param_auto_from_type (exchange_pub), /* $12 */
GNUNET_PQ_query_param_end
};
@@ -71,7 +77,7 @@ TMH_PG_insert_deposit (
"WITH md AS"
" (SELECT account_serial, merchant_serial"
" FROM merchant_accounts"
- " WHERE h_wire=$14"
+ " WHERE h_wire=$10"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
@@ -79,7 +85,7 @@ TMH_PG_insert_deposit (
", ed AS"
" (SELECT signkey_serial"
" FROM merchant_exchange_signing_keys"
- " WHERE exchange_pub=$16"
+ " WHERE exchange_pub=$12"
" ORDER BY start_date DESC"
" LIMIT 1)"
"INSERT INTO merchant_deposits"
@@ -87,20 +93,16 @@ TMH_PG_insert_deposit (
",deposit_timestamp"
",coin_pub"
",exchange_url"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",deposit_fee_val"
- ",deposit_fee_frac"
- ",refund_fee_val"
- ",refund_fee_frac"
- ",wire_fee_val"
- ",wire_fee_frac"
+ ",amount_with_fee"
+ ",deposit_fee"
+ ",refund_fee"
+ ",wire_fee"
",exchange_sig"
",signkey_serial"
",account_serial)"
" SELECT "
" order_serial"
- " ,$3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $15"
+ " ,$3, $4, $5, $6, $7, $8, $9, $11"
" ,ed.signkey_serial"
" ,md.account_serial"
" FROM merchant_contract_terms"
@@ -110,5 +112,4 @@ TMH_PG_insert_deposit (
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_deposit",
params);
-
}
diff --git a/src/backenddb/pg_insert_deposit_to_transfer.c b/src/backenddb/pg_insert_deposit_to_transfer.c
new file mode 100644
index 00000000..718d78fa
--- /dev/null
+++ b/src/backenddb/pg_insert_deposit_to_transfer.c
@@ -0,0 +1,64 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_postgres_insert_deposit_to_transfer.c
+ * @brief Implementation of the insert_deposit_to_transfer function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_deposit_to_transfer.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_deposit_to_transfer (
+ void *cls,
+ uint64_t deposit_serial,
+ const struct TALER_EXCHANGE_DepositData *dd)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&deposit_serial),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &dd->coin_contribution),
+ GNUNET_PQ_query_param_timestamp (&dd->execution_time),
+ GNUNET_PQ_query_param_auto_from_type (&dd->exchange_sig),
+ GNUNET_PQ_query_param_auto_from_type (&dd->exchange_pub),
+ GNUNET_PQ_query_param_auto_from_type (&dd->wtid),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_deposit_to_transfer",
+ "INSERT INTO merchant_deposit_to_transfer"
+ "(deposit_serial"
+ ",coin_contribution_value"
+ ",credit_serial"
+ ",execution_time"
+ ",signkey_serial"
+ ",exchange_sig"
+ ") SELECT $1, $2, credit_serial, $3, signkey_serial, $4"
+ " FROM merchant_transfers"
+ " CROSS JOIN merchant_exchange_signing_keys"
+ " WHERE exchange_pub=$5"
+ " AND wtid=$6");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_deposit_to_transfer",
+ params);
+}
diff --git a/src/backenddb/pg_insert_deposit_to_transfer.h b/src/backenddb/pg_insert_deposit_to_transfer.h
new file mode 100644
index 00000000..049cbcac
--- /dev/null
+++ b/src/backenddb/pg_insert_deposit_to_transfer.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_deposit_to_transfer.h
+ * @brief implementation of the insert_deposit_to_transfer function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_DEPOSIT_TO_TRANSFER_H
+#define PG_INSERT_DEPOSIT_TO_TRANSFER_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Insert wire transfer details for a deposit.
+ *
+ * @param cls closure
+ * @param deposit_serial serial number of the deposit
+ * @param dd deposit transfer data from the exchange to store
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_deposit_to_transfer (
+ void *cls,
+ uint64_t deposit_serial,
+ const struct TALER_EXCHANGE_DepositData *dd);
+
+
+#endif
diff --git a/src/backenddb/pg_insert_instance.c b/src/backenddb/pg_insert_instance.c
index 3777f743..82344ff4 100644
--- a/src/backenddb/pg_insert_instance.c
+++ b/src/backenddb/pg_insert_instance.c
@@ -16,6 +16,7 @@
/**
* @file backenddb/pg_insert_instance.c
* @brief Implementation of the insert_instance function for Postgres
+ * @author Christian Grothoff
* @author Iván Ávalos
*/
#include "platform.h"
@@ -44,8 +45,10 @@ TMH_PG_insert_instance (
GNUNET_PQ_query_param_uint32 (&ut32),
TALER_PQ_query_param_json (is->address),
TALER_PQ_query_param_json (is->jurisdiction),
- TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
- TALER_PQ_query_param_amount (&is->default_max_wire_fee),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &is->default_max_deposit_fee),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &is->default_max_wire_fee),
GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
GNUNET_PQ_query_param_relative_time (
&is->default_wire_transfer_delay),
@@ -80,10 +83,8 @@ TMH_PG_insert_instance (
",user_type"
",address"
",jurisdiction"
- ",default_max_deposit_fee_val"
- ",default_max_deposit_fee_frac"
- ",default_max_wire_fee_val"
- ",default_max_wire_fee_frac"
+ ",default_max_deposit_fee"
+ ",default_max_wire_fee"
",default_wire_fee_amortization"
",default_wire_transfer_delay"
",default_pay_delay"
@@ -91,7 +92,7 @@ TMH_PG_insert_instance (
",email"
",logo)"
"VALUES"
- "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)");
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)");
qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_instance",
params);
diff --git a/src/backenddb/pg_insert_pickup.c b/src/backenddb/pg_insert_pickup.c
new file mode 100644
index 00000000..5f1f91fa
--- /dev/null
+++ b/src/backenddb/pg_insert_pickup.c
@@ -0,0 +1,162 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_pickup.c
+ * @brief Implementation of the insert_pickup function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_pickup.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_pickup (
+ void *cls,
+ const char *instance_id,
+ const struct TALER_RewardIdentifierP *reward_id,
+ const struct TALER_Amount *total_picked_up,
+ const struct TALER_PickupIdentifierP *pickup_id,
+ const struct TALER_Amount *total_requested)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ PREPARE (pg,
+ "insert_pickup",
+ "INSERT INTO merchant_reward_pickups"
+ "(reward_serial"
+ ",pickup_id"
+ ",amount"
+ ") "
+ "SELECT"
+ " reward_serial, $3, $4"
+ " 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)");
+ PREPARE (pg,
+ "update_picked_up_reward",
+ "UPDATE merchant_rewards SET"
+ " picked_up=$2"
+ ",was_picked_up = (CAST($2 AS taler_amount) = amount)"
+ " WHERE reward_id = $1");
+ PREPARE (pg,
+ "lookup_picked_up_reserve",
+ "SELECT"
+ " reserve_serial"
+ ",rewards_picked_up"
+ " FROM merchant_reward_reserves"
+ " JOIN merchant_rewards USING (reserve_serial)"
+ " WHERE reward_id=$2"
+ " AND merchant_serial ="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1)");
+ PREPARE (pg,
+ "update_picked_up_reserve",
+ "UPDATE merchant_reward_reserves SET"
+ " rewards_picked_up=$2"
+ " WHERE reserve_serial = $1");
+
+ {
+ 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),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ total_requested),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_pickup",
+ params);
+ if (0 > qs)
+ return qs;
+ }
+
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reward_id),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ total_picked_up),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_picked_up_reward",
+ params);
+ if (0 > qs)
+ return qs;
+ }
+ {
+ uint64_t reserve_serial;
+ struct TALER_Amount reserve_picked_up;
+ {
+ 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 ("reserve_serial",
+ &reserve_serial),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_picked_up",
+ &reserve_picked_up),
+ GNUNET_PQ_result_spec_end
+
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_picked_up_reserve",
+ params,
+ rs);
+ if (0 > qs)
+ return qs;
+ }
+ if (0 >=
+ TALER_amount_add (&reserve_picked_up,
+ &reserve_picked_up,
+ total_requested))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&reserve_serial),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &reserve_picked_up),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_picked_up_reserve",
+ params);
+ if (0 > qs)
+ return qs;
+ }
+ }
+ return qs;
+}
diff --git a/src/backenddb/pg_insert_pickup.h b/src/backenddb/pg_insert_pickup.h
new file mode 100644
index 00000000..73a0598d
--- /dev/null
+++ b/src/backenddb/pg_insert_pickup.h
@@ -0,0 +1,57 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_postgres_insert_pickup.h
+ * @brief implementation of the postgres_insert_pickup function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_POSTGRES_INSERT_PICKUP_H
+#define PG_POSTGRES_INSERT_PICKUP_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Insert details about a reward pickup operation. The @a total_picked_up
+ * UPDATES the total amount under the @a reward_id, while the @a
+ * total_requested is the amount to be associated with this @a pickup_id.
+ * While there is usually only one pickup event that picks up the entire
+ * amount, our schema allows for wallets to pick up the amount incrementally
+ * over mulrewardle pick up operations.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param instance_id which instance gave the reward
+ * @param reward_id the unique ID for the reward
+ * @param total_picked_up how much was picked up overall at this
+ * point (includes @a total_requested)
+ * @param pickup_id unique ID for the operation
+ * @param total_requested how much is being picked up in this operation
+ * @return transaction status, usually
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_pickup (
+ void *cls,
+ const char *instance_id,
+ const struct TALER_RewardIdentifierP *reward_id,
+ const struct TALER_Amount *total_picked_up,
+ const struct TALER_PickupIdentifierP *pickup_id,
+ const struct TALER_Amount *total_requested);
+
+#endif
diff --git a/src/backenddb/pg_insert_product.c b/src/backenddb/pg_insert_product.c
index 8ee2b274..3767accc 100644
--- a/src/backenddb/pg_insert_product.c
+++ b/src/backenddb/pg_insert_product.c
@@ -16,6 +16,7 @@
/**
* @file backenddb/pg_insert_product.c
* @brief Implementation of the insert_product function for Postgres
+ * @author Christian Grothoff
* @author Iván Ávalos
*/
#include "platform.h"
@@ -40,7 +41,8 @@ TMH_PG_insert_product (void *cls,
GNUNET_PQ_query_param_string (pd->unit),
GNUNET_PQ_query_param_string (pd->image),
TALER_PQ_query_param_json (pd->taxes),
- TALER_PQ_query_param_amount (&pd->price),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &pd->price),
GNUNET_PQ_query_param_uint64 (&pd->total_stock),
TALER_PQ_query_param_json (pd->address),
GNUNET_PQ_query_param_timestamp (&pd->next_restock),
@@ -59,15 +61,14 @@ TMH_PG_insert_product (void *cls,
",unit"
",image"
",taxes"
- ",price_val"
- ",price_frac"
+ ",price"
",total_stock"
",address"
",next_restock"
",minimum_age"
")"
" SELECT merchant_serial,"
- " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13"
+ " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12"
" FROM merchant_instances"
" WHERE merchant_id=$1");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
diff --git a/src/backenddb/pg_insert_reserve.c b/src/backenddb/pg_insert_reserve.c
new file mode 100644
index 00000000..bc73b747
--- /dev/null
+++ b/src/backenddb/pg_insert_reserve.c
@@ -0,0 +1,126 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_reserve.c
+ * @brief Implementation of the insert_reserve function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_reserve.h"
+#include "pg_helper.h"
+
+/**
+ * How often do we re-try if we run into a DB serialization error?
+ */
+#define MAX_RETRIES 3
+
+
+enum TALER_ErrorCode
+TMH_PG_insert_reserve (void *cls,
+ const char *instance_id,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *exchange_url,
+ const struct TALER_Amount *initial_balance,
+ struct GNUNET_TIME_Timestamp expiration)
+{
+ struct PostgresClosure *pg = cls;
+ unsigned int retries;
+ enum GNUNET_DB_QueryStatus qs;
+
+ retries = 0;
+ check_connection (pg);
+ PREPARE (pg,
+ "insert_reserve",
+ "INSERT INTO merchant_reward_reserves"
+ "(reserve_pub"
+ ",merchant_serial"
+ ",creation_time"
+ ",expiration"
+ ",merchant_initial_balance"
+ ")"
+ "SELECT $2, merchant_serial, $3, $4, $5"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$1");
+RETRY:
+ if (MAX_RETRIES < ++retries)
+ return TALER_EC_GENERIC_DB_SOFT_FAILURE;
+ if (GNUNET_OK !=
+ TMH_PG_start (pg,
+ "insert reserve"))
+ {
+ GNUNET_break (0);
+ return TALER_EC_GENERIC_DB_START_FAILED;
+ }
+
+ /* Setup reserve */
+ {
+ struct GNUNET_TIME_Timestamp now;
+ 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_timestamp (&now),
+ GNUNET_PQ_query_param_timestamp (&expiration),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ initial_balance),
+ GNUNET_PQ_query_param_end
+ };
+
+ now = GNUNET_TIME_timestamp_get ();
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_reserve",
+ params);
+ if (0 > qs)
+ {
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ return qs;
+ }
+ }
+ /* Store private key */
+ {
+ 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_auto_from_type (reserve_priv),
+ GNUNET_PQ_query_param_string (exchange_url),
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_reserve_key",
+ params);
+ if (0 > qs)
+ {
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ return qs;
+ }
+ }
+ qs = TMH_PG_commit (pg);
+ if (0 <= qs)
+ return TALER_EC_NONE; /* success */
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ return qs;
+}
diff --git a/src/backenddb/pg_insert_reserve.h b/src/backenddb/pg_insert_reserve.h
new file mode 100644
index 00000000..41bae2ce
--- /dev/null
+++ b/src/backenddb/pg_insert_reserve.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_reserve.h
+ * @brief implementation of the insert_reserve function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_RESERVE_H
+#define PG_INSERT_RESERVE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Add @a credit to a reserve to be used for rewardping. Note that
+ * this function does not actually perform any wire transfers to
+ * credit the reserve, it merely tells the merchant backend that
+ * a reserve now exists. This has to happen before rewards can be
+ * authorized.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param instance_id which instance is the reserve tied to
+ * @param reserve_priv which reserve is topped up or created
+ * @param reserve_pub which reserve is topped up or created
+ * @param master_pub master public key of the exchange
+ * @param exchange_url what URL is the exchange reachable at where the reserve is located
+ * @param initial_balance how much money will be added to the reserve
+ * @param expiration when does the reserve expire?
+ * @return transaction status, usually
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ */
+enum TALER_ErrorCode
+TMH_PG_insert_reserve (void *cls,
+ const char *instance_id,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const char *exchange_url,
+ const struct TALER_Amount *initial_balance,
+ struct GNUNET_TIME_Timestamp expiration);
+
+#endif
diff --git a/src/backenddb/pg_insert_transfer.c b/src/backenddb/pg_insert_transfer.c
new file mode 100644
index 00000000..872cd1ff
--- /dev/null
+++ b/src/backenddb/pg_insert_transfer.c
@@ -0,0 +1,72 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_transfer.c
+ * @brief Implementation of the insert_transfer function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_transfer.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_transfer (
+ void *cls,
+ const char *instance_id,
+ const char *exchange_url,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *credit_amount,
+ const char *payto_uri,
+ bool confirmed)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (exchange_url),
+ GNUNET_PQ_query_param_auto_from_type (wtid),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ credit_amount),
+ GNUNET_PQ_query_param_string (payto_uri),
+ GNUNET_PQ_query_param_bool (confirmed),
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ PREPARE (pg,
+ "insert_transfer",
+ "INSERT INTO merchant_transfers"
+ "(exchange_url"
+ ",wtid"
+ ",credit_amount"
+ ",account_serial"
+ ",confirmed)"
+ "SELECT"
+ " $1, $2, $3, account_serial, $5"
+ " FROM merchant_accounts"
+ " WHERE payto_uri=$4"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$6)"
+ " ON CONFLICT DO NOTHING;");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_transfer",
+ params);
+}
diff --git a/src/backenddb/pg_insert_transfer.h b/src/backenddb/pg_insert_transfer.h
new file mode 100644
index 00000000..b7bffa11
--- /dev/null
+++ b/src/backenddb/pg_insert_transfer.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_postgres_insert_transfer.h
+ * @brief implementation of the postgres_insert_transfer function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_POSTGRES_INSERT_TRANSFER_H
+#define PG_POSTGRES_INSERT_TRANSFER_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+/**
+ * Insert information about a wire transfer the merchant has received.
+ *
+ * @param cls closure
+ * @param instance_id the instance that received the transfer
+ * @param exchange_url which exchange made the transfer
+ * @param wtid identifier of the wire transfer
+ * @param credit_amount how much did we receive
+ * @param payto_uri what is the merchant's bank account that received the transfer
+ * @param confirmed whether the transfer was confirmed by the merchant or
+ * was merely claimed by the exchange at this point
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_transfer (
+ void *cls,
+ const char *instance_id,
+ const char *exchange_url,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_Amount *credit_amount,
+ const char *payto_uri,
+ bool confirmed);
+
+
+#endif
diff --git a/src/backenddb/pg_insert_transfer_details.c b/src/backenddb/pg_insert_transfer_details.c
new file mode 100644
index 00000000..d79dbe90
--- /dev/null
+++ b/src/backenddb/pg_insert_transfer_details.c
@@ -0,0 +1,290 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_transfer_details.c
+ * @brief Implementation of the insert_transfer_details function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_transfer_details.h"
+#include "pg_helper.h"
+
+
+/**
+ * How often do we re-try if we run into a DB serialization error?
+ */
+#define MAX_RETRIES 3
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_transfer_details (
+ void *cls,
+ const char *instance_id,
+ const char *exchange_url,
+ const char *payto_uri,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_EXCHANGE_TransferData *td)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ uint64_t credit_serial;
+ unsigned int retries;
+
+ retries = 0;
+ check_connection (pg);
+
+ PREPARE (pg,
+ "lookup_credit_serial",
+ "SELECT"
+ " credit_serial"
+ " FROM merchant_transfers"
+ " WHERE exchange_url=$1"
+ " AND wtid=$4"
+ " AND account_serial="
+ " (SELECT account_serial"
+ " FROM merchant_accounts"
+ " WHERE payto_uri=$2"
+ " AND exchange_url=$1"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$3))");
+ PREPARE (pg,
+ "insert_transfer_signature",
+ "INSERT INTO merchant_transfer_signatures"
+ "(credit_serial"
+ ",signkey_serial"
+ ",credit_amount"
+ ",wire_fee"
+ ",execution_time"
+ ",exchange_sig) "
+ "SELECT $1, signkey_serial, $2, $3, $4, $5"
+ " FROM merchant_exchange_signing_keys"
+ " WHERE exchange_pub=$6"
+ " ORDER BY start_date DESC"
+ " LIMIT 1");
+ PREPARE (pg,
+ "insert_transfer_to_coin_mapping",
+ "INSERT INTO merchant_transfer_to_coin"
+ "(deposit_serial"
+ ",credit_serial"
+ ",offset_in_exchange_list"
+ ",exchange_deposit_value"
+ ",exchange_deposit_fee) "
+ "SELECT deposit_serial, $1, $2, $3, $4"
+ " FROM merchant_deposits"
+ " JOIN merchant_contract_terms USING (order_serial)"
+ " WHERE coin_pub=$5"
+ " AND h_contract_terms=$6"
+ " AND merchant_serial="
+ " (SELECT merchant_serial"
+ " FROM merchant_instances"
+ " WHERE merchant_id=$7)");
+ PREPARE (pg,
+ "update_wired_by_coin_pub",
+ "WITH os AS" /* select orders affected by the coin */
+ "(SELECT order_serial"
+ " FROM merchant_deposits"
+ " WHERE coin_pub=$1)"
+ "UPDATE merchant_contract_terms "
+ " SET wired=TRUE "
+ " WHERE order_serial IN "
+ " (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/
+ " WHERE NOT EXISTS "
+ " (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */
+ " JOIN os USING (order_serial)" /* filter early */
+ " WHERE deposit_serial NOT IN"
+ " (SELECT deposit_serial " /* all coins associated with order that WERE wired */
+ " FROM merchant_deposits "
+ " JOIN os USING (order_serial)" /* filter early */
+ " JOIN merchant_deposit_to_transfer USING (deposit_serial)"
+ " JOIN merchant_transfers USING (credit_serial)"
+ " WHERE confirmed=TRUE)))");
+
+RETRY:
+ if (MAX_RETRIES < ++retries)
+ return GNUNET_DB_STATUS_SOFT_ERROR;
+ if (GNUNET_OK !=
+ TMH_PG_start_read_committed (pg,
+ "insert transfer details"))
+ {
+ GNUNET_break (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* lookup credit serial */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_string (exchange_url),
+ GNUNET_PQ_query_param_string (payto_uri),
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_auto_from_type (wtid),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("credit_serial",
+ &credit_serial),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "lookup_credit_serial",
+ params,
+ rs);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
+ payto_uri,
+ TALER_amount2s (&td->total_amount),
+ qs);
+ return qs;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ TMH_PG_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'lookup_credit_serial' for account %s failed with transfer unknown\n",
+ payto_uri);
+ return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ }
+ }
+
+ /* update merchant_transfer_signatures table */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&credit_serial),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &td->total_amount),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &td->wire_fee),
+ GNUNET_PQ_query_param_timestamp (&td->execution_time),
+ GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
+ GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_transfer_signature",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_signature' failed with status %d\n",
+ qs);
+ return qs;
+ }
+ if (0 == qs)
+ {
+ TMH_PG_rollback (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_signature' failed with status %d\n",
+ qs);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+
+ /* Update transfer-coin association table */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Updating transfer-coin association table\n");
+ for (unsigned int i = 0; i<td->details_length; i++)
+ {
+ const struct TALER_TrackTransferDetails *d = &td->details[i];
+ uint64_t i64 = (uint64_t) i;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (&credit_serial),
+ GNUNET_PQ_query_param_uint64 (&i64),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &d->coin_value),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &d->coin_fee), /* deposit fee */
+ GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms),
+ GNUNET_PQ_query_param_string (instance_id),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_transfer_to_coin_mapping",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'insert_transfer_to_coin_mapping' failed with status %d\n",
+ qs);
+ return qs;
+ }
+ if (0 == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "'insert_transfer_to_coin_mapping' failed at %u: deposit unknown\n",
+ i);
+ }
+ }
+ /* Update merchant_contract_terms 'wired' status: for all coins
+ that were wired, set the respective order's "wired" status to
+ true, *if* all other deposited coins associated with that order
+ have also been wired (this time or earlier) */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Updating contract terms 'wired' status\n");
+ for (unsigned int i = 0; i<td->details_length; i++)
+ {
+ const struct TALER_TrackTransferDetails *d = &td->details[i];
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "update_wired_by_coin_pub",
+ params);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ TMH_PG_rollback (pg);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "'update_wired_by_coin_pub' failed with status %d\n",
+ qs);
+ return qs;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Committing transaction...\n");
+ qs = TMH_PG_commit (pg);
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ goto RETRY;
+ return qs;
+}
diff --git a/src/backenddb/pg_insert_transfer_details.h b/src/backenddb/pg_insert_transfer_details.h
new file mode 100644
index 00000000..8980024e
--- /dev/null
+++ b/src/backenddb/pg_insert_transfer_details.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_transfer_details.h
+ * @brief implementation of the insert_transfer_details function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_TRANSFER_DETAILS_H
+#define PG_INSERT_TRANSFER_DETAILS_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Insert information about a wire transfer the merchant has received.
+ *
+ * @param cls closure
+ * @param instance_id instance to provide transfer details for
+ * @param exchange_url which exchange made the transfer
+ * @param payto_uri what is the merchant's bank account that received the transfer
+ * @param wtid identifier of the wire transfer
+ * @param td transfer details to store
+ * @return transaction status,
+ * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
+ * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_transfer_details (
+ void *cls,
+ const char *instance_id,
+ const char *exchange_url,
+ const char *payto_uri,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const struct TALER_EXCHANGE_TransferData *td);
+
+#endif
diff --git a/src/backenddb/pg_lookup_deposits.c b/src/backenddb/pg_lookup_deposits.c
index 27647dda..01e35183 100644
--- a/src/backenddb/pg_lookup_deposits.c
+++ b/src/backenddb/pg_lookup_deposits.c
@@ -145,14 +145,10 @@ TMH_PG_lookup_deposits (
"SELECT"
" exchange_url"
",coin_pub"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",deposit_fee_val"
- ",deposit_fee_frac"
- ",refund_fee_val"
- ",refund_fee_frac"
- ",wire_fee_val"
- ",wire_fee_frac"
+ ",amount_with_fee"
+ ",deposit_fee"
+ ",refund_fee"
+ ",wire_fee"
" FROM merchant_deposits"
" WHERE order_serial="
" (SELECT order_serial"
diff --git a/src/backenddb/pg_lookup_instances.c b/src/backenddb/pg_lookup_instances.c
index 7dcbcb31..ada20134 100644
--- a/src/backenddb/pg_lookup_instances.c
+++ b/src/backenddb/pg_lookup_instances.c
@@ -392,10 +392,8 @@ TMH_PG_lookup_instances (void *cls,
",user_type"
",address"
",jurisdiction"
- ",default_max_deposit_fee_val"
- ",default_max_deposit_fee_frac"
- ",default_max_wire_fee_val"
- ",default_max_wire_fee_frac"
+ ",default_max_deposit_fee"
+ ",default_max_wire_fee"
",default_wire_fee_amortization"
",default_wire_transfer_delay"
",default_pay_delay"
@@ -447,10 +445,8 @@ TMH_PG_lookup_instance (void *cls,
",user_type"
",address"
",jurisdiction"
- ",default_max_deposit_fee_val"
- ",default_max_deposit_fee_frac"
- ",default_max_wire_fee_val"
- ",default_max_wire_fee_frac"
+ ",default_max_deposit_fee"
+ ",default_max_wire_fee"
",default_wire_fee_amortization"
",default_wire_transfer_delay"
",default_pay_delay"
diff --git a/src/backenddb/pg_lookup_product.c b/src/backenddb/pg_lookup_product.c
index d9eb0915..04c27dff 100644
--- a/src/backenddb/pg_lookup_product.c
+++ b/src/backenddb/pg_lookup_product.c
@@ -87,8 +87,7 @@ TMH_PG_lookup_product (void *cls,
" description"
",description_i18n"
",unit"
- ",price_val"
- ",price_frac"
+ ",price"
",taxes"
",total_stock"
",total_sold"
diff --git a/src/backenddb/pg_lookup_refunds.c b/src/backenddb/pg_lookup_refunds.c
index bc943162..e53860c2 100644
--- a/src/backenddb/pg_lookup_refunds.c
+++ b/src/backenddb/pg_lookup_refunds.c
@@ -128,8 +128,7 @@ TMH_PG_lookup_refunds (
"lookup_refunds",
"SELECT"
" coin_pub"
- ",refund_amount_val"
- ",refund_amount_frac"
+ ",refund_amount"
" FROM merchant_refunds"
" WHERE order_serial="
" (SELECT order_serial"
diff --git a/src/backenddb/pg_lookup_reserves.c b/src/backenddb/pg_lookup_reserves.c
index 6e27fa6f..2e503bb3 100644
--- a/src/backenddb/pg_lookup_reserves.c
+++ b/src/backenddb/pg_lookup_reserves.c
@@ -194,14 +194,10 @@ TMH_PG_lookup_reserves (void *cls,
" reserve_pub"
",creation_time"
",expiration"
- ",merchant_initial_balance_val"
- ",merchant_initial_balance_frac"
- ",exchange_initial_balance_val"
- ",exchange_initial_balance_frac"
- ",rewards_committed_val"
- ",rewards_committed_frac"
- ",rewards_picked_up_val"
- ",rewards_picked_up_frac"
+ ",merchant_initial_balance"
+ ",exchange_initial_balance"
+ ",rewards_committed"
+ ",rewards_picked_up"
",reserve_priv IS NOT NULL AS active"
" FROM merchant_reward_reserves"
" FULL OUTER JOIN merchant_reward_reserve_keys USING (reserve_serial)"
diff --git a/src/backenddb/pg_lookup_transfers.c b/src/backenddb/pg_lookup_transfers.c
index 02faca2b..9fb5cdcb 100644
--- a/src/backenddb/pg_lookup_transfers.c
+++ b/src/backenddb/pg_lookup_transfers.c
@@ -180,8 +180,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_time_payto_asc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -205,8 +204,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_time_payto_desc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -250,8 +248,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_time_asc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -274,8 +271,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_time_desc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -320,8 +316,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_payto_asc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -346,8 +341,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_payto_desc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -390,8 +384,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_desc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
@@ -415,8 +408,7 @@ TMH_PG_lookup_transfers (void *cls,
PREPARE (pg,
"lookup_transfers_asc",
"SELECT"
- " mt.credit_amount_val"
- ",mt.credit_amount_frac"
+ " mt.credit_amount"
",wtid"
",merchant_accounts.payto_uri"
",exchange_url"
diff --git a/src/backenddb/pg_refund_coin.c b/src/backenddb/pg_refund_coin.c
index 3b124612..afbcebcc 100644
--- a/src/backenddb/pg_refund_coin.c
+++ b/src/backenddb/pg_refund_coin.c
@@ -50,8 +50,7 @@ TMH_PG_refund_coin (void *cls,
",refund_timestamp"
",coin_pub"
",reason"
- ",refund_amount_val"
- ",refund_amount_frac"
+ ",refund_amount"
") "
"SELECT "
" order_serial"
@@ -59,8 +58,7 @@ TMH_PG_refund_coin (void *cls,
",$3"
",coin_pub"
",$5"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
+ ",amount_with_fee"
" FROM merchant_deposits"
" WHERE coin_pub=$4"
" AND order_serial="
diff --git a/src/backenddb/pg_select_open_transfers.c b/src/backenddb/pg_select_open_transfers.c
index ac8b2b63..44fdd695 100644
--- a/src/backenddb/pg_select_open_transfers.c
+++ b/src/backenddb/pg_select_open_transfers.c
@@ -144,8 +144,7 @@ TMH_PG_select_open_transfers (void *cls,
",exchange_url"
",payto_uri"
",wtid"
- ",credit_amount_val"
- ",credit_amount_frac"
+ ",credit_amount"
",ready_time AS next_attempt"
" FROM merchant_transfers"
" JOIN merchant_accounts"
diff --git a/src/backenddb/pg_set_transfer_status_to_confirmed.c b/src/backenddb/pg_set_transfer_status_to_confirmed.c
index 700965a1..767cd0d6 100644
--- a/src/backenddb/pg_set_transfer_status_to_confirmed.c
+++ b/src/backenddb/pg_set_transfer_status_to_confirmed.c
@@ -39,7 +39,8 @@ TMH_PG_set_transfer_status_to_confirmed (
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_string (exchange_url),
- TALER_PQ_query_param_amount (amount),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ amount),
GNUNET_PQ_query_param_end
};
@@ -49,8 +50,7 @@ TMH_PG_set_transfer_status_to_confirmed (
"UPDATE merchant_transfers SET"
" confirmed=TRUE"
" WHERE wtid=$2"
- " AND credit_amount_val=$4"
- " AND credit_amount_frac=$5"
+ " AND credit_amount=cast($4 AS taler_amount)"
" AND exchange_url=$3"
" AND account_serial IN"
" (SELECT account_serial"
diff --git a/src/backenddb/pg_store_wire_fee_by_exchange.c b/src/backenddb/pg_store_wire_fee_by_exchange.c
new file mode 100644
index 00000000..f552b1fe
--- /dev/null
+++ b/src/backenddb/pg_store_wire_fee_by_exchange.c
@@ -0,0 +1,76 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_store_wire_fee_by_exchange.c
+ * @brief Implementation of the store_wire_fee_by_exchange function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_store_wire_fee_by_exchange.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_store_wire_fee_by_exchange (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const struct GNUNET_HashCode *h_wire_method,
+ const struct TALER_WireFeeSet *fees,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_pub),
+ GNUNET_PQ_query_param_auto_from_type (h_wire_method),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &fees->wire),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &fees->closing),
+ GNUNET_PQ_query_param_timestamp (&start_date),
+ GNUNET_PQ_query_param_timestamp (&end_date),
+ GNUNET_PQ_query_param_auto_from_type (master_sig),
+ GNUNET_PQ_query_param_end
+ };
+
+ /* no preflight check here, run in its own transaction by the caller */
+ PREPARE (pg,
+ "insert_wire_fee",
+ "INSERT INTO merchant_exchange_wire_fees"
+ "(master_pub"
+ ",h_wire_method"
+ ",wire_fee"
+ ",closing_fee"
+ ",start_date"
+ ",end_date"
+ ",master_sig)"
+ " VALUES "
+ "($1, $2, $3, $4, $5, $6, $7)"
+ " ON CONFLICT DO NOTHING");
+ check_connection (pg);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Storing wire fee for %s starting at %s of %s\n",
+ TALER_B2S (master_pub),
+ GNUNET_TIME_timestamp2s (start_date),
+ TALER_amount2s (&fees->wire));
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_wire_fee",
+ params);
+}
diff --git a/src/backenddb/pg_store_wire_fee_by_exchange.h b/src/backenddb/pg_store_wire_fee_by_exchange.h
new file mode 100644
index 00000000..53ff4c3d
--- /dev/null
+++ b/src/backenddb/pg_store_wire_fee_by_exchange.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_postgres_store_wire_fee_by_exchange.h
+ * @brief implementation of the postgres_store_wire_fee_by_exchange function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_POSTGRES_STORE_WIRE_FEE_BY_EXCHANGE_H
+#define PG_POSTGRES_STORE_WIRE_FEE_BY_EXCHANGE_H
+
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler_merchantdb_plugin.h"
+
+
+/**
+ * Store 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 h_wire_method hash of wire method
+ * @param fees the fee charged
+ * @param start_date start of fee being used
+ * @param end_date end of fee being used
+ * @param master_sig signature of exchange over fee structure
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_store_wire_fee_by_exchange (
+ void *cls,
+ const struct TALER_MasterPublicKeyP *master_pub,
+ const struct GNUNET_HashCode *h_wire_method,
+ const struct TALER_WireFeeSet *fees,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_MasterSignatureP *master_sig);
+
+#endif
diff --git a/src/backenddb/pg_update_instance.c b/src/backenddb/pg_update_instance.c
index 5de6032d..b94fda08 100644
--- a/src/backenddb/pg_update_instance.c
+++ b/src/backenddb/pg_update_instance.c
@@ -16,6 +16,7 @@
/**
* @file backenddb/pg_update_instance.c
* @brief Implementation of the update_instance function for Postgres
+ * @author Christian Grothoff
* @author Iván Ávalos
*/
#include "platform.h"
@@ -36,8 +37,10 @@ TMH_PG_update_instance (void *cls,
GNUNET_PQ_query_param_string (is->name),
TALER_PQ_query_param_json (is->address),
TALER_PQ_query_param_json (is->jurisdiction),
- TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
- TALER_PQ_query_param_amount (&is->default_max_wire_fee),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &is->default_max_deposit_fee),
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &is->default_max_wire_fee),
GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
GNUNET_PQ_query_param_relative_time (
&is->default_wire_transfer_delay),
@@ -62,17 +65,15 @@ TMH_PG_update_instance (void *cls,
" merchant_name=$2"
",address=$3"
",jurisdiction=$4"
- ",default_max_deposit_fee_val=$5"
- ",default_max_deposit_fee_frac=$6"
- ",default_max_wire_fee_val=$7"
- ",default_max_wire_fee_frac=$8"
- ",default_wire_fee_amortization=$9"
- ",default_wire_transfer_delay=$10"
- ",default_pay_delay=$11"
- ",website=$12"
- ",email=$13"
- ",logo=$14"
- ",user_type=$15"
+ ",default_max_deposit_fee=$5"
+ ",default_max_wire_fee=$6"
+ ",default_wire_fee_amortization=$7"
+ ",default_wire_transfer_delay=$8"
+ ",default_pay_delay=$9"
+ ",website=$10"
+ ",email=$11"
+ ",logo=$12"
+ ",user_type=$13"
" WHERE merchant_id=$1");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_instance",
diff --git a/src/backenddb/pg_update_product.c b/src/backenddb/pg_update_product.c
index b571a8fe..ea845781 100644
--- a/src/backenddb/pg_update_product.c
+++ b/src/backenddb/pg_update_product.c
@@ -16,6 +16,7 @@
/**
* @file backenddb/pg_update_product.c
* @brief Implementation of the update_product function for Postgres
+ * @author Christian Grothoff
* @author Iván Ávalos
*/
#include "platform.h"
@@ -40,8 +41,9 @@ TMH_PG_update_product (void *cls,
GNUNET_PQ_query_param_string (pd->unit),
GNUNET_PQ_query_param_string (pd->image), /* $6 */
TALER_PQ_query_param_json (pd->taxes),
- TALER_PQ_query_param_amount (&pd->price), /* $8+$9 */
- GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $10 */
+ TALER_PQ_query_param_amount_tuple (pg->conn,
+ &pd->price), /* $8 */
+ GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $9 */
GNUNET_PQ_query_param_uint64 (&pd->total_lost),
TALER_PQ_query_param_json (pd->address),
GNUNET_PQ_query_param_timestamp (&pd->next_restock),
@@ -65,20 +67,19 @@ TMH_PG_update_product (void *cls,
",unit=$5"
",image=$6"
",taxes=$7"
- ",price_val=$8"
- ",price_frac=$9"
- ",total_stock=$10"
- ",total_lost=$11"
- ",address=$12"
- ",next_restock=$13"
- ",minimum_age=$14"
+ ",price=$8"
+ ",total_stock=$9"
+ ",total_lost=$10"
+ ",address=$11"
+ ",next_restock=$12"
+ ",minimum_age=$13"
" WHERE merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND product_id=$2"
- " AND total_stock <= $10"
- " AND total_lost <= $11");
+ " AND total_stock <= $9"
+ " AND total_lost <= $10");
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_product",
params);
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c
index f129b90b..016cb4e2 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -84,6 +84,15 @@
#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"
/**
@@ -234,126 +243,6 @@ check_connection (struct PostgresClosure *pg)
/**
- * Start a transaction.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param name unique name identifying the transaction (for debugging),
- * must point to a constant
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-postgres_start (void *cls,
- const char *name)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
-
- check_connection (pg);
- postgres_preflight (pg);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Starting merchant DB transaction `%s'\n",
- name);
- if (GNUNET_OK !=
- GNUNET_PQ_exec_statements (pg->conn,
- es))
- {
- TALER_LOG_ERROR ("Failed to start transaction\n");
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- pg->transaction_name = name;
- return GNUNET_OK;
-}
-
-
-/**
- * Start a transaction in 'read committed' mode.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param name unique name identifying the transaction (for debugging),
- * must point to a constant
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-postgres_start_read_committed (void *cls,
- const char *name)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
-
- check_connection (pg);
- postgres_preflight (pg);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Starting merchant DB transaction %s (READ COMMITTED)\n",
- name);
- if (GNUNET_OK !=
- GNUNET_PQ_exec_statements (pg->conn,
- es))
- {
- TALER_LOG_ERROR ("Failed to start transaction\n");
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- pg->transaction_name = name;
- return GNUNET_OK;
-}
-
-
-/**
- * Roll back the current transaction of a database connection.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- */
-static void
-postgres_rollback (void *cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_ExecuteStatement es[] = {
- GNUNET_PQ_make_execute ("ROLLBACK"),
- GNUNET_PQ_EXECUTE_STATEMENT_END
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Rolling back merchant DB transaction `%s'\n",
- pg->transaction_name);
- GNUNET_break (GNUNET_OK ==
- GNUNET_PQ_exec_statements (pg->conn,
- es));
- pg->transaction_name = NULL;
-}
-
-
-/**
- * Commit the current transaction of a database connection.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_commit (void *cls)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_end
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Committing merchant DB transaction %s\n",
- pg->transaction_name);
- pg->transaction_name = NULL;
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "end_transaction",
- params);
-}
-
-
-/**
* Closure for lookup_deposits_by_order_cb().
*/
struct LookupDepositsByOrderContext
@@ -616,37 +505,6 @@ postgres_lookup_transfer_details_by_order (
/**
- * Insert wire transfer details for a deposit.
- *
- * @param cls closure
- * @param deposit_serial serial number of the deposit
- * @param dd deposit transfer data from the exchange to store
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_deposit_to_transfer (
- void *cls,
- uint64_t deposit_serial,
- const struct TALER_EXCHANGE_DepositData *dd)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&deposit_serial),
- TALER_PQ_query_param_amount (&dd->coin_contribution),
- GNUNET_PQ_query_param_timestamp (&dd->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&dd->exchange_sig),
- GNUNET_PQ_query_param_auto_from_type (&dd->exchange_pub),
- GNUNET_PQ_query_param_auto_from_type (&dd->wtid),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_deposit_to_transfer",
- params);
-}
-
-
-/**
* Set 'wired' status for an order to 'true'.
*
* @param cls closure
@@ -670,452 +528,6 @@ postgres_mark_order_wired (void *cls,
/**
- * Closure for #process_refund_cb().
- */
-struct FindRefundContext
-{
-
- /**
- * Plugin context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Updated to reflect total amount refunded so far.
- */
- struct TALER_Amount refunded_amount;
-
- /**
- * Set to the largest refund transaction ID encountered.
- */
- uint64_t max_rtransaction_id;
-
- /**
- * Set to true on hard errors.
- */
- bool err;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure, our `struct FindRefundContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-process_refund_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct FindRefundContext *ictx = cls;
- struct PostgresClosure *pg = ictx->pg;
-
- for (unsigned int i = 0; i<num_results; i++)
- {
- /* Sum up existing refunds */
- struct TALER_Amount acc;
- uint64_t rtransaction_id;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
- &acc),
- GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
- &rtransaction_id),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- ictx->err = true;
- return;
- }
- if (0 >
- TALER_amount_add (&ictx->refunded_amount,
- &ictx->refunded_amount,
- &acc))
- {
- GNUNET_break (0);
- ictx->err = true;
- return;
- }
- ictx->max_rtransaction_id = GNUNET_MAX (ictx->max_rtransaction_id,
- rtransaction_id);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Found refund of %s\n",
- TALER_amount2s (&acc));
- }
-}
-
-
-/**
- * Closure for #process_deposits_for_refund_cb().
- */
-struct InsertRefundContext
-{
- /**
- * Used to provide a connection to the db
- */
- struct PostgresClosure *pg;
-
- /**
- * Amount to which increase the refund for this contract
- */
- const struct TALER_Amount *refund;
-
- /**
- * Human-readable reason behind this refund
- */
- const char *reason;
-
- /**
- * Transaction status code.
- */
- enum TALER_MERCHANTDB_RefundStatus rs;
-};
-
-
-/**
- * Data extracted per coin.
- */
-struct RefundCoinData
-{
-
- /**
- * Public key of a coin.
- */
- struct TALER_CoinSpendPublicKeyP coin_pub;
-
- /**
- * Amount deposited for this coin.
- */
- struct TALER_Amount deposited_with_fee;
-
- /**
- * Amount refunded already for this coin.
- */
- struct TALER_Amount refund_amount;
-
- /**
- * Order serial (actually not really per-coin).
- */
- uint64_t order_serial;
-
- /**
- * Maximum rtransaction_id for this coin so far.
- */
- uint64_t max_rtransaction_id;
-
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
- *
- * @param cls closure, our `struct InsertRefundContext`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-process_deposits_for_refund_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct InsertRefundContext *ctx = cls;
- struct PostgresClosure *pg = ctx->pg;
- struct TALER_Amount current_refund;
- struct RefundCoinData rcd[GNUNET_NZL (num_results)];
- struct GNUNET_TIME_Timestamp now;
-
- now = GNUNET_TIME_timestamp_get ();
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (ctx->refund->currency,
- &current_refund));
- memset (rcd, 0, sizeof (rcd));
- /* Pass 1: Collect amount of existing refunds into current_refund.
- * Also store existing refunded amount for each deposit in deposit_refund. */
- for (unsigned int i = 0; i<num_results; i++)
- {
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
- &rcd[i].coin_pub),
- GNUNET_PQ_result_spec_uint64 ("order_serial",
- &rcd[i].order_serial),
- TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
- &rcd[i].deposited_with_fee),
- GNUNET_PQ_result_spec_end
- };
- struct FindRefundContext ictx = {
- .pg = pg
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- }
-
- {
- enum GNUNET_DB_QueryStatus ires;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
- GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
- GNUNET_PQ_query_param_end
- };
-
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (ctx->refund->currency,
- &ictx.refunded_amount));
- ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
- "find_refunds_by_coin",
- params,
- &process_refund_cb,
- &ictx);
- if ( (ictx.err) ||
- (GNUNET_DB_STATUS_HARD_ERROR == ires) )
- {
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- }
- if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
- {
- ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
- return;
- }
- }
- if (0 >
- TALER_amount_add (&current_refund,
- &current_refund,
- &ictx.refunded_amount))
- {
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- }
- rcd[i].refund_amount = ictx.refunded_amount;
- rcd[i].max_rtransaction_id = ictx.max_rtransaction_id;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Existing refund for coin %s is %s\n",
- TALER_B2S (&rcd[i].coin_pub),
- TALER_amount2s (&ictx.refunded_amount));
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Total existing refund is %s\n",
- TALER_amount2s (&current_refund));
-
- /* stop immediately if we are 'done' === amount already
- * refunded. */
- if (0 >= TALER_amount_cmp (ctx->refund,
- &current_refund))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Existing refund of %s at or above requested refund. Finished early.\n",
- TALER_amount2s (&current_refund));
- ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
- return;
- }
-
- /* Phase 2: Try to increase current refund until it matches desired refund */
- for (unsigned int i = 0; i<num_results; i++)
- {
- const struct TALER_Amount *increment;
- struct TALER_Amount left;
- struct TALER_Amount remaining_refund;
-
- /* How much of the coin is left after the existing refunds? */
- if (0 >
- TALER_amount_subtract (&left,
- &rcd[i].deposited_with_fee,
- &rcd[i].refund_amount))
- {
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- }
-
- if ( (0 == left.value) &&
- (0 == left.fraction) )
- {
- /* coin was fully refunded, move to next coin */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Coin %s fully refunded, moving to next coin\n",
- TALER_B2S (&rcd[i].coin_pub));
- continue;
- }
-
- rcd[i].max_rtransaction_id++;
- /* How much of the refund is still to be paid back? */
- if (0 >
- TALER_amount_subtract (&remaining_refund,
- ctx->refund,
- &current_refund))
- {
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- }
-
- /* By how much will we increase the refund for this coin? */
- if (0 >= TALER_amount_cmp (&remaining_refund,
- &left))
- {
- /* remaining_refund <= left */
- increment = &remaining_refund;
- }
- else
- {
- increment = &left;
- }
-
- if (0 >
- TALER_amount_add (&current_refund,
- &current_refund,
- increment))
- {
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- }
-
- /* actually run the refund */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Coin %s deposit amount is %s\n",
- TALER_B2S (&rcd[i].coin_pub),
- TALER_amount2s (&rcd[i].deposited_with_fee));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Coin %s refund will be incremented by %s\n",
- TALER_B2S (&rcd[i].coin_pub),
- TALER_amount2s (increment));
- {
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
- GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already inc'ed */
- GNUNET_PQ_query_param_timestamp (&now),
- GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
- GNUNET_PQ_query_param_string (ctx->reason),
- TALER_PQ_query_param_amount (increment),
- GNUNET_PQ_query_param_end
- };
-
- check_connection (pg);
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_refund",
- params);
- switch (qs)
- {
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
- return;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
- return;
- default:
- ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs;
- break;
- }
- }
-
- /* stop immediately if we are done */
- if (0 == TALER_amount_cmp (ctx->refund,
- &current_refund))
- {
- ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
- return;
- }
- }
-
- /**
- * We end up here if not all of the refund has been covered.
- * Although this should be checked as the business should never
- * issue a refund bigger than the contract's actual price, we cannot
- * rely upon the frontend being correct.
- */
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "The refund of %s is bigger than the order's value\n",
- TALER_amount2s (ctx->refund));
- ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH;
-}
-
-
-/**
- * Function called when some backoffice staff decides to award or
- * increase the refund on an existing contract. This function
- * MUST be called from within a transaction scope setup by the
- * caller as it executes mulrewardle SQL statements.
- *
- * @param cls closure
- * @param instance_id instance identifier
- * @param order_id the order to increase the refund for
- * @param refund maximum refund to return to the customer for this contract
- * @param reason 0-terminated UTF-8 string giving the reason why the customer
- * got a refund (free form, business-specific)
- * @return transaction status
- * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we
- * were originally paid and thus the transaction failed;
- * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
- * regardless of whether it actually increased the refund beyond
- * what was already refunded (idempotency!)
- */
-static enum TALER_MERCHANTDB_RefundStatus
-postgres_increase_refund (void *cls,
- const char *instance_id,
- const char *order_id,
- const struct TALER_Amount *refund,
- const char *reason)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_string (order_id),
- GNUNET_PQ_query_param_end
- };
- struct InsertRefundContext ctx = {
- .pg = pg,
- .refund = refund,
- .reason = reason
- };
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Asked to refund %s on order %s\n",
- TALER_amount2s (refund),
- order_id);
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "find_deposits_for_refund",
- params,
- &process_deposits_for_refund_cb,
- &ctx);
- switch (qs)
- {
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* never paid, means we clearly cannot refund anything */
- return TALER_MERCHANTDB_RS_NO_SUCH_ORDER;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return TALER_MERCHANTDB_RS_SOFT_ERROR;
- case GNUNET_DB_STATUS_HARD_ERROR:
- return TALER_MERCHANTDB_RS_HARD_ERROR;
- default:
- /* Got one or more deposits */
- return ctx.rs;
- }
-}
-
-
-/**
* Closure for #lookup_refunds_detailed_cb().
*/
struct LookupRefundsDetailedContext
@@ -1365,47 +777,6 @@ postgres_lookup_order_by_fulfillment (void *cls,
/**
- * Insert information about a wire transfer the merchant has received.
- *
- * @param cls closure
- * @param instance_id the instance that received the transfer
- * @param exchange_url which exchange made the transfer
- * @param wtid identifier of the wire transfer
- * @param credit_amount how much did we receive
- * @param payto_uri what is the merchant's bank account that received the transfer
- * @param confirmed whether the transfer was confirmed by the merchant or
- * was merely claimed by the exchange at this point
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_transfer (
- void *cls,
- const char *instance_id,
- const char *exchange_url,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const struct TALER_Amount *credit_amount,
- const char *payto_uri,
- bool confirmed)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (exchange_url),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- TALER_PQ_query_param_amount (credit_amount),
- GNUNET_PQ_query_param_string (payto_uri),
- GNUNET_PQ_query_param_bool (confirmed),
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_end
- };
-
- check_connection (pg);
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_transfer",
- params);
-}
-
-
-/**
* Delete information about a transfer. Note that transfers
* confirmed by the exchange cannot be deleted anymore.
*
@@ -1503,204 +874,6 @@ postgres_lookup_account (void *cls,
/**
- * Insert information about a wire transfer the merchant has received.
- *
- * @param cls closure
- * @param instance_id instance to provide transfer details for
- * @param exchange_url which exchange made the transfer
- * @param payto_uri what is the merchant's bank account that received the transfer
- * @param wtid identifier of the wire transfer
- * @param td transfer details to store
- * @return transaction status,
- * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
- * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_transfer_details (
- void *cls,
- const char *instance_id,
- const char *exchange_url,
- const char *payto_uri,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const struct TALER_EXCHANGE_TransferData *td)
-{
- struct PostgresClosure *pg = cls;
- enum GNUNET_DB_QueryStatus qs;
- uint64_t credit_serial;
- unsigned int retries;
-
- retries = 0;
- check_connection (pg);
-RETRY:
- if (MAX_RETRIES < ++retries)
- return GNUNET_DB_STATUS_SOFT_ERROR;
- if (GNUNET_OK !=
- postgres_start_read_committed (pg,
- "insert transfer details"))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
-
- /* lookup credit serial */
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (exchange_url),
- GNUNET_PQ_query_param_string (payto_uri),
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_auto_from_type (wtid),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_uint64 ("credit_serial",
- &credit_serial),
- GNUNET_PQ_result_spec_end
- };
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "lookup_credit_serial",
- params,
- rs);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
- payto_uri,
- TALER_amount2s (&td->total_amount),
- qs);
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- postgres_rollback (pg);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "'lookup_credit_serial' for account %s failed with transfer unknown\n",
- payto_uri);
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
- }
-
- /* update merchant_transfer_signatures table */
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&credit_serial),
- TALER_PQ_query_param_amount (&td->total_amount),
- TALER_PQ_query_param_amount (&td->wire_fee),
- GNUNET_PQ_query_param_timestamp (&td->execution_time),
- GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
- GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_transfer_signature",
- params);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "'insert_transfer_signature' failed with status %d\n",
- qs);
- return qs;
- }
- if (0 == qs)
- {
- postgres_rollback (pg);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "'insert_transfer_signature' failed with status %d\n",
- qs);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
-
- /* Update transfer-coin association table */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Updating transfer-coin association table\n");
- for (unsigned int i = 0; i<td->details_length; i++)
- {
- const struct TALER_TrackTransferDetails *d = &td->details[i];
- uint64_t i64 = (uint64_t) i;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&credit_serial),
- GNUNET_PQ_query_param_uint64 (&i64),
- TALER_PQ_query_param_amount (&d->coin_value),
- TALER_PQ_query_param_amount (&d->coin_fee), /* deposit fee */
- GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
- GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms),
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_transfer_to_coin_mapping",
- params);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "'insert_transfer_to_coin_mapping' failed with status %d\n",
- qs);
- return qs;
- }
- if (0 == qs)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "'insert_transfer_to_coin_mapping' failed at %u: deposit unknown\n",
- i);
- }
- }
- /* Update merchant_contract_terms 'wired' status: for all coins
- that were wired, set the respective order's "wired" status to
- true, *if* all other deposited coins associated with that order
- have also been wired (this time or earlier) */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Updating contract terms 'wired' status\n");
- for (unsigned int i = 0; i<td->details_length; i++)
- {
- const struct TALER_TrackTransferDetails *d = &td->details[i];
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_wired_by_coin_pub",
- params);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "'update_wired_by_coin_pub' failed with status %d\n",
- qs);
- return qs;
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Committing transaction...\n");
- qs = postgres_commit (pg);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return qs;
-}
-
-
-/**
* Obtain information about wire fees charged by an exchange,
* including signature (so we have proof).
*
@@ -2249,187 +1422,6 @@ postgres_lookup_transfer_details (
/**
- * Store 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 h_wire_method hash of wire method
- * @param fees the fee charged
- * @param start_date start of fee being used
- * @param end_date end of fee being used
- * @param master_sig signature of exchange over fee structure
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_store_wire_fee_by_exchange (
- void *cls,
- const struct TALER_MasterPublicKeyP *master_pub,
- const struct GNUNET_HashCode *h_wire_method,
- const struct TALER_WireFeeSet *fees,
- struct GNUNET_TIME_Timestamp start_date,
- struct GNUNET_TIME_Timestamp end_date,
- const struct TALER_MasterSignatureP *master_sig)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (master_pub),
- GNUNET_PQ_query_param_auto_from_type (h_wire_method),
- TALER_PQ_query_param_amount (&fees->wire),
- TALER_PQ_query_param_amount (&fees->closing),
- GNUNET_PQ_query_param_timestamp (&start_date),
- GNUNET_PQ_query_param_timestamp (&end_date),
- GNUNET_PQ_query_param_auto_from_type (master_sig),
- GNUNET_PQ_query_param_end
- };
-
- /* no preflight check here, run in its own transaction by the caller */
- check_connection (pg);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Storing wire fee for %s starting at %s of %s\n",
- TALER_B2S (master_pub),
- GNUNET_TIME_timestamp2s (start_date),
- TALER_amount2s (&fees->wire));
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_wire_fee",
- params);
-}
-
-
-/**
- * Add @a credit to a reserve to be used for rewardping. Note that
- * this function does not actually perform any wire transfers to
- * credit the reserve, it merely tells the merchant backend that
- * a reserve now exists. This has to happen before rewards can be
- * authorized.
- *
- * @param cls closure, typically a connection to the db
- * @param instance_id which instance is the reserve tied to
- * @param reserve_priv which reserve is topped up or created
- * @param reserve_pub which reserve is topped up or created
- * @param master_pub master public key of the exchange
- * @param exchange_url what URL is the exchange reachable at where the reserve is located
- * @param initial_balance how much money will be added to the reserve
- * @param expiration when does the reserve expire?
- * @return transaction status, usually
- * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
- */
-static enum TALER_ErrorCode
-postgres_insert_reserve (void *cls,
- const char *instance_id,
- const struct TALER_ReservePrivateKeyP *reserve_priv,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_MasterPublicKeyP *master_pub,
- const char *exchange_url,
- const struct TALER_Amount *initial_balance,
- struct GNUNET_TIME_Timestamp expiration)
-{
- struct PostgresClosure *pg = cls;
- unsigned int retries;
- enum GNUNET_DB_QueryStatus qs;
-
- retries = 0;
- check_connection (pg);
-RETRY:
- if (MAX_RETRIES < ++retries)
- return TALER_EC_GENERIC_DB_SOFT_FAILURE;
- if (GNUNET_OK !=
- postgres_start (pg,
- "insert reserve"))
- {
- GNUNET_break (0);
- return TALER_EC_GENERIC_DB_START_FAILED;
- }
-
- /* Setup reserve */
- {
- struct GNUNET_TIME_Timestamp now;
- 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_timestamp (&now),
- GNUNET_PQ_query_param_timestamp (&expiration),
- TALER_PQ_query_param_amount (initial_balance),
- GNUNET_PQ_query_param_end
- };
-
- now = GNUNET_TIME_timestamp_get ();
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_reserve",
- params);
- if (0 > qs)
- {
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return qs;
- }
- }
- /* Store private key */
- {
- 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_auto_from_type (reserve_priv),
- GNUNET_PQ_query_param_string (exchange_url),
- GNUNET_PQ_query_param_auto_from_type (master_pub),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_reserve_key",
- params);
- if (0 > qs)
- {
- postgres_rollback (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return qs;
- }
- }
- qs = postgres_commit (pg);
- if (0 <= qs)
- return TALER_EC_NONE; /* success */
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- return qs;
-}
-
-
-/**
- * Confirms @a credit as the amount the exchange claims to have received and
- * thus really 'activates' the reserve. This has to happen before rewards can
- * be authorized.
- *
- * @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 topped up or created
- * @param initial_exchange_balance how much money was be added to the reserve
- * according to the exchange
- * @return transaction status, usually
- * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
- */
-static enum GNUNET_DB_QueryStatus
-postgres_activate_reserve (void *cls,
- const char *instance_id,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_Amount *initial_exchange_balance)
-{
- 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),
- TALER_PQ_query_param_amount (initial_exchange_balance),
- GNUNET_PQ_query_param_end
- };
-
- return GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "activate_reserve",
- params);
-}
-
-
-/**
* Closure for #lookup_pending_reserves_cb.
*/
struct LookupPendingReservesContext
@@ -2795,366 +1787,6 @@ postgres_purge_reserve (void *cls,
/**
- * Closure for #lookup_reserve_for_reward_cb().
- */
-struct LookupReserveForRewardContext
-{
- /**
- * Postgres context.
- */
- struct PostgresClosure *pg;
-
- /**
- * Public key of the reserve we found.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- /**
- * How much money must be left in the reserve.
- */
- struct TALER_Amount required_amount;
-
- /**
- * Set to the expiration time of the reserve we found.
- * #GNUNET_TIME_UNIT_FOREVER_ABS if we found none.
- */
- struct GNUNET_TIME_Timestamp expiration;
-
- /**
- * Error status.
- */
- enum TALER_ErrorCode ec;
-
- /**
- * Did we find a good reserve?
- */
- bool ok;
-};
-
-
-/**
- * How long must a reserve be at least still valid before we use
- * it for a reward?
- */
-#define MIN_EXPIRATION GNUNET_TIME_UNIT_HOURS
-
-
-/**
- * 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 LookupReserveForRewardContext *`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-lookup_reserve_for_reward_cb (void *cls,
- PGresult *result,
- unsigned int num_results)
-{
- struct LookupReserveForRewardContext *lac = cls;
- struct PostgresClosure *pg = lac->pg;
-
- for (unsigned int i = 0; i < num_results; i++)
- {
- struct TALER_ReservePublicKeyP reserve_pub;
- struct TALER_Amount committed_amount;
- struct TALER_Amount remaining;
- struct TALER_Amount initial_balance;
- struct GNUNET_TIME_Timestamp expiration;
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
- &reserve_pub),
- TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
- &initial_balance),
- TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_committed",
- &committed_amount),
- GNUNET_PQ_result_spec_timestamp ("expiration",
- &expiration),
- GNUNET_PQ_result_spec_end
- };
-
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- i))
- {
- GNUNET_break (0);
- lac->ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
- return;
- }
- if (0 >
- TALER_amount_subtract (&remaining,
- &initial_balance,
- &committed_amount))
- {
- GNUNET_break (0);
- continue;
- }
- if (0 >
- TALER_amount_cmp (&remaining,
- &lac->required_amount))
- {
- /* insufficient balance */
- if (lac->ok)
- continue; /* got another reserve */
- lac->ec =
- TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS;
- continue;
- }
- if ( (! GNUNET_TIME_absolute_is_never (lac->expiration.abs_time)) &&
- GNUNET_TIME_timestamp_cmp (expiration,
- >,
- lac->expiration) &&
- GNUNET_TIME_relative_cmp (
- GNUNET_TIME_absolute_get_remaining (lac->expiration.abs_time),
- >,
- MIN_EXPIRATION) )
- {
- /* reserve expired */
- if (lac->ok)
- continue; /* got another reserve */
- lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_EXPIRED;
- continue;
- }
- lac->ok = true;
- lac->ec = TALER_EC_NONE;
- lac->expiration = expiration;
- lac->reserve_pub = reserve_pub;
- }
-}
-
-
-/**
- * Authorize a reward over @a amount from reserve @a reserve_pub. Remember
- * the authorization under @a reward_id for later, together with the
- * @a justification.
- *
- * @param cls closure, typically a connection to the db
- * @param instance_id which instance should generate the reward
- * @param reserve_pub which reserve is debited, NULL to pick one in the DB
- * @param amount how high is the reward (with fees)
- * @param justification why was the reward approved
- * @param next_url where to send the URL post reward pickup
- * @param[out] reward_id set to the unique ID for the reward
- * @param[out] expiration set to when the reward expires
- * @return transaction status,
- * #TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
- * #TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_NOT_FOUND if the reserve is not known
- * #TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
- * #TALER_EC_GENERIC_DB_START_FAILED on hard DB errors
- * #TALER_EC_GENERIC_DB_FETCH_FAILED on hard DB errors
- * #TALER_EC_GENERIC_DB_STORE_FAILED on hard DB errors
- * #TALER_EC_GENERIC_DB_INVARIANT_FAILURE on hard DB errors
- * #TALER_EC_GENERIC_DB_SOFT_FAILURE soft DB errors (client should retry)
- * #TALER_EC_NONE upon success
- */
-static enum TALER_ErrorCode
-postgres_authorize_reward (void *cls,
- const char *instance_id,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_Amount *amount,
- const char *justification,
- const char *next_url,
- struct TALER_RewardIdentifierP *reward_id,
- struct GNUNET_TIME_Timestamp *expiration)
-{
- struct PostgresClosure *pg = cls;
- unsigned int retries = 0;
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_Amount rewards_committed;
- struct TALER_Amount exchange_initial_balance;
- const struct TALER_ReservePublicKeyP *reserve_pubp;
- struct LookupReserveForRewardContext lac = {
- .pg = pg,
- .required_amount = *amount,
- .expiration = GNUNET_TIME_UNIT_FOREVER_TS
- };
-
- check_connection (pg);
-RETRY:
- reserve_pubp = reserve_pub;
- if (MAX_RETRIES < ++retries)
- {
- GNUNET_break (0);
- return
- TALER_EC_GENERIC_DB_SOFT_FAILURE;
- }
- if (GNUNET_OK !=
- postgres_start (pg,
- "authorize reward"))
- {
- GNUNET_break (0);
- return TALER_EC_GENERIC_DB_START_FAILED;
- }
- if (NULL == reserve_pubp)
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
- "lookup_reserve_for_reward",
- params,
- &lookup_reserve_for_reward_cb,
- &lac);
- switch (qs)
- {
- case GNUNET_DB_STATUS_SOFT_ERROR:
- postgres_rollback (pg);
- goto RETRY;
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- postgres_rollback (pg);
- return TALER_EC_GENERIC_DB_FETCH_FAILED;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- postgres_rollback (pg);
- return TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_NOT_FOUND;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- default:
- break;
- }
- if (TALER_EC_NONE != lac.ec)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Enabling reward reserved failed with status %d\n",
- lac.ec);
- postgres_rollback (pg);
- return lac.ec;
- }
- GNUNET_assert (lac.ok);
- reserve_pubp = &lac.reserve_pub;
- }
-
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- GNUNET_PQ_result_spec_timestamp ("expiration",
- expiration),
- TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_committed",
- &rewards_committed),
- TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
- &exchange_initial_balance),
- GNUNET_PQ_result_spec_end
- };
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "lookup_reserve_status",
- params,
- rs);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- postgres_rollback (pg);
- goto RETRY;
- }
- if (qs < 0)
- {
- GNUNET_break (0);
- postgres_rollback (pg);
- return TALER_EC_GENERIC_DB_FETCH_FAILED;
- }
- if (0 == qs)
- {
- postgres_rollback (pg);
- return TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_RESERVE_NOT_FOUND;
- }
- }
- {
- struct TALER_Amount remaining;
-
- if (0 >
- TALER_amount_subtract (&remaining,
- &exchange_initial_balance,
- &rewards_committed))
- {
- GNUNET_break (0);
- postgres_rollback (pg);
- return TALER_EC_GENERIC_DB_INVARIANT_FAILURE;
- }
- if (0 >
- TALER_amount_cmp (&remaining,
- amount))
- {
- postgres_rollback (pg);
- return TALER_EC_MERCHANT_PRIVATE_POST_REWARD_AUTHORIZE_INSUFFICIENT_FUNDS;
- }
- }
- GNUNET_assert (0 <=
- TALER_amount_add (&rewards_committed,
- &rewards_committed,
- amount));
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
- TALER_PQ_query_param_amount (&rewards_committed),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_reserve_rewards_committed",
- params);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- postgres_rollback (pg);
- goto RETRY;
- }
- if (qs < 0)
- {
- GNUNET_break (0);
- postgres_rollback (pg);
- return TALER_EC_GENERIC_DB_STORE_FAILED;
- }
- }
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
- reward_id,
- sizeof (*reward_id));
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_string (instance_id),
- GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
- GNUNET_PQ_query_param_auto_from_type (reward_id),
- GNUNET_PQ_query_param_string (justification),
- GNUNET_PQ_query_param_string (next_url),
- GNUNET_PQ_query_param_timestamp (expiration),
- TALER_PQ_query_param_amount (amount),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_reward",
- params);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- postgres_rollback (pg);
- goto RETRY;
- }
- if (qs < 0)
- {
- GNUNET_break (0);
- postgres_rollback (pg);
- return TALER_EC_GENERIC_DB_STORE_FAILED;
- }
- }
- qs = postgres_commit (pg);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- if (qs < 0)
- {
- GNUNET_break (0);
- postgres_rollback (pg);
- return TALER_EC_GENERIC_DB_COMMIT_FAILED;
- }
- return TALER_EC_NONE;
-}
-
-
-/**
* Closure for #lookup_signatures_cb().
*/
struct LookupSignaturesContext
@@ -3653,117 +2285,6 @@ postgres_lookup_reward_details (void *cls,
/**
- * Insert details about a reward pickup operation. The @a total_picked_up
- * UPDATES the total amount under the @a reward_id, while the @a
- * total_requested is the amount to be associated with this @a pickup_id.
- * While there is usually only one pickup event that picks up the entire
- * amount, our schema allows for wallets to pick up the amount incrementally
- * over mulrewardle pick up operations.
- *
- * @param cls closure, typically a connection to the db
- * @param instance_id which instance gave the reward
- * @param reward_id the unique ID for the reward
- * @param total_picked_up how much was picked up overall at this
- * point (includes @a total_requested)
- * @param pickup_id unique ID for the operation
- * @param total_requested how much is being picked up in this operation
- * @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 (void *cls,
- const char *instance_id,
- const struct TALER_RewardIdentifierP *reward_id,
- const struct TALER_Amount *total_picked_up,
- const struct TALER_PickupIdentifierP *pickup_id,
- const struct TALER_Amount *total_requested)
-{
- struct PostgresClosure *pg = cls;
- 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_auto_from_type (pickup_id),
- TALER_PQ_query_param_amount (total_requested),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "insert_pickup",
- params);
- if (0 > qs)
- return qs;
- }
-
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reward_id),
- TALER_PQ_query_param_amount (total_picked_up),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_picked_up_reward",
- params);
- if (0 > qs)
- return qs;
- }
- {
- uint64_t reserve_serial;
- struct TALER_Amount reserve_picked_up;
- {
- 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 ("reserve_serial",
- &reserve_serial),
- TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_picked_up",
- &reserve_picked_up),
- GNUNET_PQ_result_spec_end
-
- };
-
- qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "lookup_picked_up_reserve",
- params,
- rs);
- if (0 > qs)
- return qs;
- }
- if (0 >=
- TALER_amount_add (&reserve_picked_up,
- &reserve_picked_up,
- total_requested))
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
-
- {
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_uint64 (&reserve_serial),
- TALER_PQ_query_param_amount (&reserve_picked_up),
- GNUNET_PQ_query_param_end
- };
-
- qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
- "update_picked_up_reserve",
- params);
- if (0 > qs)
- return qs;
- }
- }
- return qs;
-}
-
-
-/**
* Insert blind signature obtained from the exchange during a
* reward pickup operation.
*
@@ -4812,10 +3333,8 @@ postgres_connect (void *cls)
" deposit_serial"
",exchange_url"
",h_wire"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",deposit_fee_val"
- ",deposit_fee_frac"
+ ",amount_with_fee"
+ ",deposit_fee"
",coin_pub"
" FROM merchant_deposits"
" JOIN merchant_accounts USING (account_serial)"
@@ -4826,10 +3345,8 @@ postgres_connect (void *cls)
" md.deposit_serial"
",md.exchange_url"
",mt.wtid"
- ",exchange_deposit_value_val"
- ",exchange_deposit_value_frac"
- ",exchange_deposit_fee_val"
- ",exchange_deposit_fee_frac"
+ ",exchange_deposit_value"
+ ",exchange_deposit_fee"
",deposit_timestamp"
",mt.confirmed AS transfer_confirmed"
" FROM merchant_transfer_to_coin"
@@ -4839,64 +3356,11 @@ postgres_connect (void *cls)
" (SELECT deposit_serial"
" FROM merchant_deposits"
" WHERE order_serial=$1)"),
- /* for postgres_insert_deposit_to_transfer() */
- GNUNET_PQ_make_prepare ("insert_deposit_to_transfer",
- "INSERT INTO merchant_deposit_to_transfer"
- "(deposit_serial"
- ",coin_contribution_value_val"
- ",coin_contribution_value_frac"
- ",credit_serial"
- ",execution_time"
- ",signkey_serial"
- ",exchange_sig"
- ") SELECT $1, $2, $3, credit_serial, $4, signkey_serial, $5"
- " FROM merchant_transfers"
- " CROSS JOIN merchant_exchange_signing_keys"
- " WHERE exchange_pub=$6"
- " AND wtid=$7"),
/* for postgres_mark_order_wired() */
GNUNET_PQ_make_prepare ("mark_order_wired",
"UPDATE merchant_contract_terms SET"
" wired=true"
" WHERE order_serial=$1"),
- /* for process_refund_cb() used in postgres_increase_refund() */
- GNUNET_PQ_make_prepare ("find_refunds_by_coin",
- "SELECT"
- " refund_amount_val"
- ",refund_amount_frac"
- ",rtransaction_id"
- " FROM merchant_refunds"
- " WHERE coin_pub=$1"
- " AND order_serial=$2"),
- /* for process_deposits_for_refund_cb() used in postgres_increase_refund() */
- GNUNET_PQ_make_prepare ("insert_refund",
- "INSERT INTO merchant_refunds"
- "(order_serial"
- ",rtransaction_id"
- ",refund_timestamp"
- ",coin_pub"
- ",reason"
- ",refund_amount_val"
- ",refund_amount_frac"
- ") VALUES"
- "($1, $2, $3, $4, $5, $6, $7)"),
- /* for postgres_increase_refund() */
- GNUNET_PQ_make_prepare ("find_deposits_for_refund",
- "SELECT"
- " coin_pub"
- ",order_serial"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- " FROM merchant_deposits"
- " WHERE order_serial="
- " (SELECT order_serial"
- " FROM merchant_contract_terms"
- " WHERE order_id=$2"
- " AND paid=TRUE"
- " AND merchant_serial="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$1))"),
/* for postgres_lookup_refunds_detailed() */
GNUNET_PQ_make_prepare ("lookup_refunds_detailed",
"SELECT"
@@ -4906,8 +3370,7 @@ postgres_connect (void *cls)
",merchant_deposits.exchange_url"
",rtransaction_id"
",reason"
- ",refund_amount_val"
- ",refund_amount_frac"
+ ",refund_amount"
",merchant_refund_proofs.exchange_sig IS NULL AS pending"
" FROM merchant_refunds"
" JOIN merchant_deposits USING (order_serial, coin_pub)"
@@ -4952,24 +3415,6 @@ postgres_connect (void *cls)
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
- /* for postgres_insert_transfer() */
- GNUNET_PQ_make_prepare ("insert_transfer",
- "INSERT INTO merchant_transfers"
- "(exchange_url"
- ",wtid"
- ",credit_amount_val"
- ",credit_amount_frac"
- ",account_serial"
- ",confirmed)"
- "SELECT"
- " $1, $2, $3, $4, account_serial, $6"
- " FROM merchant_accounts"
- " WHERE payto_uri=$5"
- " AND merchant_serial="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$7)"
- " ON CONFLICT DO NOTHING;"),
/* for postgres_delete_transfer() */
GNUNET_PQ_make_prepare ("delete_transfer",
"DELETE FROM merchant_transfers"
@@ -5006,84 +3451,11 @@ postgres_connect (void *cls)
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
- /* for postgres_insert_transfer_details() */
- GNUNET_PQ_make_prepare ("lookup_credit_serial",
- "SELECT"
- " credit_serial"
- " FROM merchant_transfers"
- " WHERE exchange_url=$1"
- " AND wtid=$4"
- " AND account_serial="
- " (SELECT account_serial"
- " FROM merchant_accounts"
- " WHERE payto_uri=$2"
- " AND exchange_url=$1"
- " AND merchant_serial="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$3))"),
- /* for postgres_insert_transfer_details() */
- GNUNET_PQ_make_prepare ("insert_transfer_signature",
- "INSERT INTO merchant_transfer_signatures"
- "(credit_serial"
- ",signkey_serial"
- ",credit_amount_val"
- ",credit_amount_frac"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",execution_time"
- ",exchange_sig) "
- "SELECT $1, signkey_serial, $2, $3, $4, $5, $6, $7"
- " FROM merchant_exchange_signing_keys"
- " WHERE exchange_pub=$8"
- " ORDER BY start_date DESC"
- " LIMIT 1"),
- /* for postgres_insert_transfer_details() */
- GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping",
- "INSERT INTO merchant_transfer_to_coin"
- "(deposit_serial"
- ",credit_serial"
- ",offset_in_exchange_list"
- ",exchange_deposit_value_val"
- ",exchange_deposit_value_frac"
- ",exchange_deposit_fee_val"
- ",exchange_deposit_fee_frac) "
- "SELECT deposit_serial, $1, $2, $3, $4, $5, $6"
- " FROM merchant_deposits"
- " JOIN merchant_contract_terms USING (order_serial)"
- " WHERE coin_pub=$7"
- " AND h_contract_terms=$8"
- " AND merchant_serial="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$9)"),
- /* for postgres_insert_transfer_details() */
- GNUNET_PQ_make_prepare ("update_wired_by_coin_pub",
- "WITH os AS" /* select orders affected by the coin */
- "(SELECT order_serial"
- " FROM merchant_deposits"
- " WHERE coin_pub=$1)"
- "UPDATE merchant_contract_terms "
- " SET wired=TRUE "
- " WHERE order_serial IN "
- " (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/
- " WHERE NOT EXISTS "
- " (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */
- " JOIN os USING (order_serial)" /* filter early */
- " WHERE deposit_serial NOT IN"
- " (SELECT deposit_serial " /* all coins associated with order that WERE wired */
- " FROM merchant_deposits "
- " JOIN os USING (order_serial)" /* filter early */
- " JOIN merchant_deposit_to_transfer USING (deposit_serial)"
- " JOIN merchant_transfers USING (credit_serial)"
- " WHERE confirmed=TRUE)))"),
/* for postgres_lookup_wire_fee() */
GNUNET_PQ_make_prepare ("lookup_wire_fee",
"SELECT"
- " wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
+ " wire_fee"
+ ",closing_fee"
",start_date"
",end_date"
",master_sig"
@@ -5096,14 +3468,10 @@ postgres_connect (void *cls)
GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin",
"SELECT"
" exchange_url"
- ",amount_with_fee_val"
- ",amount_with_fee_frac"
- ",deposit_fee_val"
- ",deposit_fee_frac"
- ",refund_fee_val"
- ",refund_fee_frac"
- ",wire_fee_val"
- ",wire_fee_frac"
+ ",amount_with_fee"
+ ",deposit_fee"
+ ",refund_fee"
+ ",wire_fee"
",h_wire"
",deposit_timestamp"
",refund_deadline"
@@ -5122,12 +3490,9 @@ postgres_connect (void *cls)
/* for postgres_lookup_transfer() */
GNUNET_PQ_make_prepare ("lookup_transfer",
"SELECT"
- " mt.credit_amount_val AS credit_amount_val"
- ",mt.credit_amount_frac AS credit_amount_frac"
- ",mts.credit_amount_val AS exchange_amount_val"
- ",mts.credit_amount_frac AS exchange_amount_frac"
- ",wire_fee_val"
- ",wire_fee_frac"
+ " mt.credit_amount AS credit_amount"
+ ",mts.credit_amount AS exchange_amount"
+ ",wire_fee"
",execution_time"
",verified"
" FROM merchant_transfers mt"
@@ -5141,10 +3506,8 @@ postgres_connect (void *cls)
GNUNET_PQ_make_prepare ("lookup_transfer_summary",
"SELECT"
" order_id"
- ",exchange_deposit_value_val"
- ",exchange_deposit_value_frac"
- ",exchange_deposit_fee_val"
- ",exchange_deposit_fee_frac"
+ ",exchange_deposit_value"
+ ",exchange_deposit_fee"
" FROM merchant_transfers"
" JOIN merchant_transfer_to_coin USING (credit_serial)"
" JOIN merchant_deposits USING (deposit_serial)"
@@ -5157,54 +3520,14 @@ postgres_connect (void *cls)
" merchant_contract_terms.h_contract_terms"
",merchant_transfer_to_coin.offset_in_exchange_list"
",merchant_deposits.coin_pub"
- ",exchange_deposit_value_val"
- ",exchange_deposit_value_frac"
- ",exchange_deposit_fee_val"
- ",exchange_deposit_fee_frac"
+ ",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_store_wire_fee_by_exchange() */
- GNUNET_PQ_make_prepare ("insert_wire_fee",
- "INSERT INTO merchant_exchange_wire_fees"
- "(master_pub"
- ",h_wire_method"
- ",wire_fee_val"
- ",wire_fee_frac"
- ",closing_fee_val"
- ",closing_fee_frac"
- ",start_date"
- ",end_date"
- ",master_sig)"
- " VALUES "
- "($1, $2, $3, $4, $5, $6, $7, $8, $9)"
- " ON CONFLICT DO NOTHING"),
- /* For postgres_insert_reserve() */
- GNUNET_PQ_make_prepare ("insert_reserve",
- "INSERT INTO merchant_reward_reserves"
- "(reserve_pub"
- ",merchant_serial"
- ",creation_time"
- ",expiration"
- ",merchant_initial_balance_val"
- ",merchant_initial_balance_frac"
- ")"
- "SELECT $2, merchant_serial, $3, $4, $5, $6"
- " FROM merchant_instances"
- " WHERE merchant_id=$1"),
- /* For postgres_activate_reserve() */
- GNUNET_PQ_make_prepare ("activate_reserve",
- "UPDATE merchant_reward_reserves SET"
- " exchange_initial_balance_val=$3"
- ",exchange_initial_balance_frac=$4"
- " WHERE reserve_pub=$2"
- " AND merchant_serial="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$1)"),
/* For postgres_insert_reserve() */
GNUNET_PQ_make_prepare ("insert_reserve_key",
"INSERT INTO merchant_reward_reserve_keys"
@@ -5226,26 +3549,20 @@ postgres_connect (void *cls)
" reserve_pub"
",merchant_id"
",exchange_url"
- ",merchant_initial_balance_val"
- ",merchant_initial_balance_frac"
+ ",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_val=0"
- " AND exchange_initial_balance_frac=0"),
+ " 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_val"
- ",merchant_initial_balance_frac"
- ",exchange_initial_balance_val"
- ",exchange_initial_balance_frac"
- ",rewards_committed_val"
- ",rewards_committed_frac"
- ",rewards_picked_up_val"
- ",rewards_picked_up_frac"
+ ",merchant_initial_balance"
+ ",exchange_initial_balance"
+ ",rewards_committed"
+ ",rewards_picked_up"
",reserve_priv IS NOT NULL AS active"
",exchange_url"
",master_pub"
@@ -5261,8 +3578,7 @@ postgres_connect (void *cls)
"SELECT"
" justification"
",reward_id"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM merchant_rewards"
" WHERE reserve_serial ="
" (SELECT reserve_serial"
@@ -5293,65 +3609,6 @@ postgres_connect (void *cls)
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
- /* For postgres_authorize_reward() */
- GNUNET_PQ_make_prepare ("lookup_reserve_for_reward",
- "SELECT"
- " reserve_pub"
- ",expiration"
- ",exchange_initial_balance_val"
- ",exchange_initial_balance_frac"
- ",rewards_committed_val"
- ",rewards_committed_frac"
- " FROM merchant_reward_reserves"
- " WHERE"
- " merchant_serial ="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$1)"),
-
- /* For postgres_authorize_reward() */
- GNUNET_PQ_make_prepare ("lookup_reserve_status",
- "SELECT"
- " expiration"
- ",exchange_initial_balance_val"
- ",exchange_initial_balance_frac"
- ",rewards_committed_val"
- ",rewards_committed_frac"
- " FROM merchant_reward_reserves"
- " WHERE reserve_pub = $2"
- " AND merchant_serial ="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$1)"),
- /* For postgres_authorize_reward() */
- GNUNET_PQ_make_prepare ("update_reserve_rewards_committed",
- "UPDATE merchant_reward_reserves SET"
- " rewards_committed_val=$3"
- ",rewards_committed_frac=$4"
- " WHERE reserve_pub = $2"
- " AND merchant_serial ="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$1)"),
- /* For postgres_authorize_reward() */
- GNUNET_PQ_make_prepare ("insert_reward",
- "INSERT INTO merchant_rewards"
- "(reserve_serial"
- ",reward_id"
- ",justification"
- ",next_url"
- ",expiration"
- ",amount_val"
- ",amount_frac"
- ") "
- "SELECT"
- " reserve_serial, $3, $4, $5, $6, $7, $8"
- " 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"
@@ -5379,10 +3636,8 @@ postgres_connect (void *cls)
/* For postgres_lookup_reward() */
GNUNET_PQ_make_prepare ("lookup_reward",
"SELECT"
- " amount_val"
- ",amount_frac"
- ",picked_up_val"
- ",picked_up_frac"
+ " amount"
+ ",picked_up"
",merchant_rewards.expiration"
",exchange_url"
",next_url"
@@ -5400,8 +3655,7 @@ postgres_connect (void *cls)
"SELECT"
" reward_serial"
",reward_id"
- ",amount_val"
- ",amount_frac"
+ ",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"
@@ -5418,8 +3672,7 @@ postgres_connect (void *cls)
"SELECT"
" reward_serial"
",reward_id"
- ",amount_val"
- ",amount_frac"
+ ",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"
@@ -5436,8 +3689,7 @@ postgres_connect (void *cls)
"SELECT"
" reward_serial"
",reward_id"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE merchant_serial ="
@@ -5454,8 +3706,7 @@ postgres_connect (void *cls)
"SELECT"
" reward_serial"
",reward_id"
- ",amount_val"
- ",amount_frac"
+ ",amount"
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE merchant_serial ="
@@ -5472,10 +3723,8 @@ postgres_connect (void *cls)
GNUNET_PQ_make_prepare ("lookup_reward_details",
"SELECT"
" reward_serial"
- ",amount_val"
- ",amount_frac"
- ",picked_up_val"
- ",picked_up_frac"
+ ",amount"
+ ",picked_up"
",justification"
",merchant_rewards.expiration"
",reserve_pub"
@@ -5490,56 +3739,12 @@ postgres_connect (void *cls)
GNUNET_PQ_make_prepare ("lookup_pickup_details",
"SELECT"
" pickup_id"
- ",amount_val"
- ",amount_frac"
+ ",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() */
- GNUNET_PQ_make_prepare ("insert_pickup",
- "INSERT INTO merchant_reward_pickups"
- "(reward_serial"
- ",pickup_id"
- ",amount_val"
- ",amount_frac"
- ") "
- "SELECT"
- " reward_serial, $3, $4, $5"
- " 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_insert_pickup() */
- GNUNET_PQ_make_prepare ("update_picked_up_reward",
- "UPDATE merchant_rewards SET"
- " picked_up_val=$2"
- ",picked_up_frac=$3"
- ",was_picked_up = ($2 = amount_val AND $3 = amount_frac)"
- " WHERE reward_id = $1"),
- /* for postgres_insert_pickup() */
- GNUNET_PQ_make_prepare ("lookup_picked_up_reserve",
- "SELECT"
- " reserve_serial"
- ",rewards_picked_up_val"
- ",rewards_picked_up_frac"
- " FROM merchant_reward_reserves"
- " JOIN merchant_rewards USING (reserve_serial)"
- " WHERE reward_id=$2"
- " AND merchant_serial ="
- " (SELECT merchant_serial"
- " FROM merchant_instances"
- " WHERE merchant_id=$1)"),
- /* for postgres_insert_pickup() */
- GNUNET_PQ_make_prepare ("update_picked_up_reserve",
- "UPDATE merchant_reward_reserves SET"
- " rewards_picked_up_val=$2"
- ",rewards_picked_up_frac=$3"
- " WHERE reserve_serial = $1"),
/* for postgres_insert_pickup_blind_signature() */
GNUNET_PQ_make_prepare ("insert_pickup_blind_signature",
"INSERT INTO merchant_reward_pickup_signatures"
@@ -5812,10 +4017,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
plugin->event_listen_cancel = &postgres_event_listen_cancel;
plugin->event_notify = &postgres_event_notify;
plugin->preflight = &postgres_preflight;
- plugin->start = &postgres_start;
- plugin->start_read_committed = &postgres_start_read_committed;
- plugin->rollback = &postgres_rollback;
- plugin->commit = &postgres_commit;
+ 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
@@ -5901,18 +4106,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
plugin->lookup_deposits_by_order = &postgres_lookup_deposits_by_order;
plugin->lookup_transfer_details_by_order =
&postgres_lookup_transfer_details_by_order;
- plugin->insert_deposit_to_transfer = &postgres_insert_deposit_to_transfer;
plugin->mark_order_wired = &postgres_mark_order_wired;
- plugin->increase_refund = &postgres_increase_refund;
+ 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->insert_transfer = &postgres_insert_transfer;
plugin->delete_transfer = &postgres_delete_transfer;
plugin->check_transfer_exists = &postgres_check_transfer_exists;
plugin->lookup_account = &postgres_lookup_account;
- plugin->insert_transfer_details = &postgres_insert_transfer_details;
plugin->lookup_wire_fee = &postgres_lookup_wire_fee;
plugin->lookup_deposits_by_contract_and_coin =
&postgres_lookup_deposits_by_contract_and_coin;
@@ -5931,21 +4134,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
= &TMH_PG_update_wirewatch_progress;
plugin->select_wirewatch_accounts
= &TMH_PG_select_wirewatch_accounts;
- plugin->store_wire_fee_by_exchange = &postgres_store_wire_fee_by_exchange;
- plugin->insert_reserve = &postgres_insert_reserve;
- plugin->activate_reserve = &postgres_activate_reserve;
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->authorize_reward = &postgres_authorize_reward;
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 = &postgres_insert_pickup;
plugin->insert_pickup_blind_signature =
&postgres_insert_pickup_blind_signature;
plugin->select_open_transfers
@@ -5954,6 +4152,22 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
= &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;
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1689183704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1689183704
deleted file mode 100644
index 372ba048..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1689183704
+++ /dev/null
@@ -1,2 +0,0 @@
-1P6GϞ;5k
-?܆lG \ No newline at end of file
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1689788204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1689788204
deleted file mode 100644
index 9ed7497a..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1689788204
+++ /dev/null
@@ -1 +0,0 @@
-]n`)[U. G1U#U  \ No newline at end of file
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1689183704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1689183704
deleted file mode 100644
index b90d281d..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1689183704
+++ /dev/null
@@ -1 +0,0 @@
-?MK D. tl ,ūn>ޞ c \ No newline at end of file
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1689788204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1689788204
deleted file mode 100644
index 2e48c73c..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1689788204
+++ /dev/null
@@ -1 +0,0 @@
-UC@Ao(fK1 \ No newline at end of file
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1689183704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1689183704
deleted file mode 100644
index 1553a847..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1689183704
+++ /dev/null
@@ -1 +0,0 @@
-[R:`4(@M0nf1 \ No newline at end of file
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1689788204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1689788204
deleted file mode 100644
index 61a01ee0..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1689788204
+++ /dev/null
@@ -1,2 +0,0 @@
-x
-Ԁx퍅/1zr{:Bܚ \ No newline at end of file
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689183704 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689183704
deleted file mode 100644
index 5503cb10..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689183704
+++ /dev/null
Binary files differ
diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689788204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689788204
deleted file mode 100644
index 0b1958a1..00000000
--- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689788204
+++ /dev/null
@@ -1 +0,0 @@
-s:-A /"q8b4ǪI͈Di( \ No newline at end of file