gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: use amount tuples


From: gnunet
Subject: [taler-merchant] branch master updated: use amount tuples
Date: Fri, 28 Jul 2023 18:29:31 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new edcc5502 use amount tuples
edcc5502 is described below

commit edcc550215759e91eca93279f8be2d596dba60c4
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Jul 28 18:29:23 2023 +0200

    use amount tuples
---
 contrib/wallet-core                                |    2 +-
 src/backenddb/Makefile.am                          |   12 +-
 src/backenddb/merchant-0001.sql                    |  105 +-
 src/backenddb/pg_activate_reserve.c                |   56 +
 src/backenddb/pg_activate_reserve.h                |   48 +
 src/backenddb/pg_authorize_reward.c                |  422 ++++
 src/backenddb/pg_authorize_reward.h                |   64 +
 src/backenddb/pg_helper.c                          |  121 ++
 src/backenddb/pg_helper.h                          |   49 +-
 src/backenddb/pg_increase_refund.c                 |  491 +++++
 src/backenddb/pg_increase_refund.h                 |   56 +
 src/backenddb/pg_insert_deposit.c                  |   41 +-
 src/backenddb/pg_insert_deposit_to_transfer.c      |   64 +
 src/backenddb/pg_insert_deposit_to_transfer.h      |   44 +
 src/backenddb/pg_insert_instance.c                 |   15 +-
 src/backenddb/pg_insert_pickup.c                   |  162 ++
 src/backenddb/pg_insert_pickup.h                   |   57 +
 src/backenddb/pg_insert_product.c                  |    9 +-
 src/backenddb/pg_insert_reserve.c                  |  126 ++
 src/backenddb/pg_insert_reserve.h                  |   56 +
 ..._status_to_confirmed.c => pg_insert_transfer.c} |   60 +-
 src/backenddb/pg_insert_transfer.h                 |   52 +
 src/backenddb/pg_insert_transfer_details.c         |  290 +++
 src/backenddb/pg_insert_transfer_details.h         |   51 +
 src/backenddb/pg_lookup_deposits.c                 |   12 +-
 src/backenddb/pg_lookup_instances.c                |   12 +-
 src/backenddb/pg_lookup_product.c                  |    3 +-
 src/backenddb/pg_lookup_refunds.c                  |    3 +-
 src/backenddb/pg_lookup_reserves.c                 |   12 +-
 src/backenddb/pg_lookup_transfers.c                |   24 +-
 src/backenddb/pg_refund_coin.c                     |    6 +-
 src/backenddb/pg_select_open_transfers.c           |    3 +-
 .../pg_set_transfer_status_to_confirmed.c          |    6 +-
 src/backenddb/pg_store_wire_fee_by_exchange.c      |   76 +
 src/backenddb/pg_store_wire_fee_by_exchange.h      |   52 +
 src/backenddb/pg_update_instance.c                 |   27 +-
 src/backenddb/pg_update_product.c                  |   23 +-
 src/backenddb/plugin_merchantdb_postgres.c         | 2038 ++------------------
 .../exchange-secmod-cs/keys/coin_eur_1/1689183704  |    2 -
 .../exchange-secmod-cs/keys/coin_eur_1/1689788204  |    1 -
 .../exchange-secmod-cs/keys/coin_eur_5/1689183704  |    1 -
 .../exchange-secmod-cs/keys/coin_eur_5/1689788204  |    1 -
 .../keys/coin_eur_ct_1/1689183704                  |    1 -
 .../keys/coin_eur_ct_1/1689788204                  |    2 -
 .../keys/coin_eur_ct_10/1689183704                 |  Bin 32 -> 0 bytes
 .../keys/coin_eur_ct_10/1689788204                 |    1 -
 46 files changed, 2639 insertions(+), 2120 deletions(-)

diff --git a/contrib/wallet-core b/contrib/wallet-core
index 4664263e..7079bce1 160000
--- a/contrib/wallet-core
+++ b/contrib/wallet-core
@@ -1 +1 @@
-Subproject commit 4664263ea1a430ea70e6be34ed0c7d9946aa4e27
+Subproject commit 7079bce1ad2640e44561f56b46d5f00758df8e5d
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_set_transfer_status_to_confirmed.c 
b/src/backenddb/pg_insert_transfer.c
similarity index 51%
copy from src/backenddb/pg_set_transfer_status_to_confirmed.c
copy to src/backenddb/pg_insert_transfer.c
index 700965a1..872cd1ff 100644
--- a/src/backenddb/pg_set_transfer_status_to_confirmed.c
+++ b/src/backenddb/pg_insert_transfer.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   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
@@ -14,53 +14,59 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_set_transfer_status_to_confirmed.c
- * @brief Implementation of the set_transfer_status_to_confirmed function for 
Postgres
+ * @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_set_transfer_status_to_confirmed.h"
+#include "pg_insert_transfer.h"
 #include "pg_helper.h"
 
 
 enum GNUNET_DB_QueryStatus
-TMH_PG_set_transfer_status_to_confirmed (
+TMH_PG_insert_transfer (
   void *cls,
   const char *instance_id,
   const char *exchange_url,
   const struct TALER_WireTransferIdentifierRawP *wtid,
-  const struct TALER_Amount *amount)
+  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 (instance_id),
-    GNUNET_PQ_query_param_auto_from_type (wtid),
     GNUNET_PQ_query_param_string (exchange_url),
-    TALER_PQ_query_param_amount (amount),
+    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,
-           "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 exchange_url=$3"
-           "   AND account_serial IN"
-           "   (SELECT account_serial"
-           "     FROM merchant_accounts"
-           "    WHERE merchant_serial ="
-           "      (SELECT merchant_serial"
-           "         FROM merchant_instances"
-           "        WHERE merchant_id=$1));");
-  return GNUNET_PQ_eval_prepared_non_select (
-    pg->conn,
-    "set_transfer_status_to_confirmed",
-    params);
+           "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"
 
 
 /**
@@ -233,126 +242,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().
  */
@@ -615,37 +504,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'.
  *
@@ -669,452 +527,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().
  */
@@ -1364,47 +776,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.
@@ -1459,244 +830,46 @@ postgres_check_transfer_exists (void *cls,
     GNUNET_PQ_result_spec_end
   };
 
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "check_transfer_exists",
-                                                   params,
-                                                   rs);
-}
-
-
-/**
- * Lookup account serial by payto URI.
- *
- * @param cls closure
- * @param instance_id instance to lookup the account from
- * @param payto_uri what is the merchant's bank account to lookup
- * @param[out] account_serial serial number of the account
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_lookup_account (void *cls,
-                         const char *instance_id,
-                         const char *payto_uri,
-                         uint64_t *account_serial)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (payto_uri),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_uint64 ("account_serial",
-                                  account_serial),
-    GNUNET_PQ_result_spec_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "lookup_account",
-                                                   params,
-                                                   rs);
-}
-
-
-/**
- * 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
-    };
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "check_transfer_exists",
+                                                   params,
+                                                   rs);
+}
 
-    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;
+/**
+ * Lookup account serial by payto URI.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup the account from
+ * @param payto_uri what is the merchant's bank account to lookup
+ * @param[out] account_serial serial number of the account
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_account (void *cls,
+                         const char *instance_id,
+                         const char *payto_uri,
+                         uint64_t *account_serial)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (payto_uri),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint64 ("account_serial",
+                                  account_serial),
+    GNUNET_PQ_result_spec_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "lookup_account",
+                                                   params,
+                                                   rs);
 }
 
 
@@ -2248,187 +1421,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.
  */
@@ -2755,402 +1747,42 @@ postgres_delete_reserve (void *cls,
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
-    GNUNET_PQ_query_param_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "delete_reserve",
-                                             params);
-}
-
-
-/**
- * Purge all of the information about a reserve, including rewards.
- *
- * @param cls closure, typically a connection to the db
- * @param instance_id which instance is the reserve tied to
- * @param reserve_pub which reserve is to be purged
- * @return transaction status, usually
- *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
- */
-static enum GNUNET_DB_QueryStatus
-postgres_purge_reserve (void *cls,
-                        const char *instance_id,
-                        const struct TALER_ReservePublicKeyP *reserve_pub)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
-    GNUNET_PQ_query_param_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "purge_reserve",
-                                             params);
-}
-
-
-/**
- * Closure for #lookup_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;
-  }
+    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "delete_reserve",
+                                             params);
 }
 
 
 /**
- * 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.
+ * Purge all of the information about a reserve, including rewards.
  *
  * @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
+ * @param instance_id which instance is the reserve tied to
+ * @param reserve_pub which reserve is to be purged
+ * @return transaction status, usually
+ *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
  */
-static enum 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)
+static enum GNUNET_DB_QueryStatus
+postgres_purge_reserve (void *cls,
+                        const char *instance_id,
+                        const struct TALER_ReservePublicKeyP *reserve_pub)
 {
   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
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+    GNUNET_PQ_query_param_end
   };
 
   check_connection (pg);
-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",
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "purge_reserve",
                                              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;
 }
 
 
@@ -3652,117 +2284,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 @@
-����1P6��G�Ϟ;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.�G�1U�#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.�t�l�,�ū�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�@���A�o�(��f�K����1��
\ 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�(@M0nf�1
\ 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�퍅�/��1�zr{:�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
Binary files 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1689183704
 and /dev/null 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 /�"q8b��4Ǫ�I���͈Di(��
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]