gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] 78/277: work on refund


From: gnunet
Subject: [taler-merchant] 78/277: work on refund
Date: Sun, 05 Jul 2020 20:49:51 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

commit 74c5fa81d74ff5d239f49f74b2bb937c03b83bcb
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat May 2 17:52:13 2020 +0200

    work on refund
---
 src/backenddb/merchant-0001.sql            |    3 +
 src/backenddb/plugin_merchantdb_postgres.c | 2477 ++++++++++++++--------------
 src/include/taler_merchantdb_plugin.h      |   68 +-
 3 files changed, 1285 insertions(+), 1263 deletions(-)

diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index 77c7905..e54e478 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -323,6 +323,9 @@ COMMENT ON TABLE merchant_deposits
   IS 'Refunds approved by the merchant (backoffice) logic, excludes abort 
refunds';
 COMMENT ON COLUMN merchant_refunds.rtransaction_id
   IS 'Needed for uniqueness in case a refund is increased for the same order';
+CREATE INDEX IF NOT EXISTS merchant_refunds_by_coin_and_order
+  ON merchant_refunds
+  (coin_pub,order_serial);
 
 CREATE TABLE IF NOT EXISTS merchant_refund_proofs
   (refund_serial BIGINT PRIMARY KEY
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 14fb9b2..00df78a 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -1979,254 +1979,31 @@ postgres_lookup_order_status (void *cls,
 }
 
 
-/* ********************* OLD API ************************** */
-
-/**
- * Retrieve proposal data given its proposal data's hashcode
- *
- * @param cls closure
- * @param contract_terms where to store the retrieved proposal data
- * @param h_contract_terms proposal data's hashcode that will be used to
- * perform the lookup
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_find_contract_terms_from_hash (
-  void *cls,
-  json_t **contract_terms,
-  const struct GNUNET_HashCode *h_contract_terms,
-  const struct TALER_MerchantPublicKeyP *merchant_pub)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    TALER_PQ_result_spec_json ("contract_terms",
-                               contract_terms),
-    GNUNET_PQ_result_spec_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   
"find_contract_terms_from_hash",
-                                                   params,
-                                                   rs);
-}
-
-
-/**
- * Retrieve proposal data given its proposal data's hashcode
- *
- * @param cls closure
- * @param contract_terms where to store the retrieved proposal data
- * @param h_contract_terms proposal data's hashcode that will be used to
- * perform the lookup
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_find_paid_contract_terms_from_hash (void *cls,
-                                             json_t **contract_terms,
-                                             const struct
-                                             GNUNET_HashCode *h_contract_terms,
-                                             const struct
-                                             TALER_MerchantPublicKeyP *
-                                             merchant_pub)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    TALER_PQ_result_spec_json ("contract_terms",
-                               contract_terms),
-    GNUNET_PQ_result_spec_end
-  };
-
-  /* no preflight check here, runs in its own transaction from
-     caller (in /pay case) */
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   
"find_paid_contract_terms_from_hash",
-                                                   params,
-                                                   rs);
-}
-
-
-/**
- * Store the order ID that was used to pay for a resource within a session.
- *
- * @param cls closure
- * @param session_id session id
- * @param fulfillment_url URL that canonically identifies the resource
- *        being paid for
- * @param order_id the order ID that was used when paying for the resource URL
- * @param merchant_pub public key of the merchant, identifying the instance
- * @return transaction status
- */
-enum GNUNET_DB_QueryStatus
-postgres_insert_session_info (void *cls,
-                              const char *session_id,
-                              const char *fulfillment_url,
-                              const char *order_id,
-                              const struct
-                              TALER_MerchantPublicKeyP *merchant_pub)
-{
-  struct PostgresClosure *pg = cls;
-
-  struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (session_id),
-    GNUNET_PQ_query_param_string (fulfillment_url),
-    GNUNET_PQ_query_param_string (order_id),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_absolute_time (&now),
-    GNUNET_PQ_query_param_end
-  };
-
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_session_info",
-                                             params);
-}
-
-
 /**
- * Retrieve the order ID that was used to pay for a resource within a session.
- *
- * @param cls closure
- * @param[out] order_id where to store the order ID that was used when
- *             paying for the resource URL
- * @param session_id session id
- * @param fulfillment_url URL that canonically identifies the resource
- *        being paid for
- * @param merchant_pub public key of the merchant, identifying the instance
- * @return transaction status
+ * Closure for #process_refund_cb().
  */
-enum GNUNET_DB_QueryStatus
-postgres_find_session_info (void *cls,
-                            char **order_id,
-                            const char *session_id,
-                            const char *fulfillment_url,
-                            const struct TALER_MerchantPublicKeyP 
*merchant_pub)
-{
-  struct PostgresClosure *pg = cls;
-
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (fulfillment_url),
-    GNUNET_PQ_query_param_string (session_id),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_string ("order_id",
-                                  order_id),
-    GNUNET_PQ_result_spec_end
-  };
-  // We don't clean up the result spec since we want
-  // to keep around the memory for order_id.
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "find_session_info",
-                                                   params,
-                                                   rs);
-}
-
-
-/**
- * Insert mapping of @a coin_pub and @a h_contract_terms to
- * corresponding @a wtid.
- *
- * @param cls closure
- * @param h_contract_terms hashcode of the proposal data paid by @a coin_pub
- * @param coin_pub public key of the coin
- * @param wtid identifier of the wire transfer in which the exchange
- *             send us the money for the coin deposit
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_store_coin_to_transfer (void *cls,
-                                 const struct GNUNET_HashCode 
*h_contract_terms,
-                                 const struct
-                                 TALER_CoinSpendPublicKeyP *coin_pub,
-                                 const struct
-                                 TALER_WireTransferIdentifierRawP *wtid)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
-    GNUNET_PQ_query_param_auto_from_type (wtid),
-    GNUNET_PQ_query_param_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_transfer",
-                                             params);
-}
-
-
-/**
- * Insert wire transfer confirmation from the exchange into the database.
- *
- * @param cls closure
- * @param exchange_url URL of the exchange
- * @param wtid identifier of the wire transfer
- * @param execution_time when was @a wtid executed
- * @param signkey_pub public key used by the exchange for @a exchange_proof
- * @param exchange_proof proof from exchange about what the deposit was for
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_store_transfer_to_proof (void *cls,
-                                  const char *exchange_url,
-                                  const struct
-                                  TALER_WireTransferIdentifierRawP *wtid,
-                                  struct GNUNET_TIME_Absolute execution_time,
-                                  const struct
-                                  TALER_ExchangePublicKeyP *signkey_pub,
-                                  const json_t *exchange_proof)
+struct FindRefundContext
 {
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (exchange_url),
-    GNUNET_PQ_query_param_auto_from_type (wtid),
-    GNUNET_PQ_query_param_absolute_time (&execution_time),
-    GNUNET_PQ_query_param_auto_from_type (signkey_pub),
-    TALER_PQ_query_param_json (exchange_proof),
-    GNUNET_PQ_query_param_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_proof",
-                                             params);
-}
 
-
-/**
- * Closure for #find_tip_authorizations_cb().
- */
-struct GetAuthorizedTipAmountContext
-{
   /**
-   * Total authorized amount.
+   * Plugin context.
    */
-  struct TALER_Amount authorized_amount;
+  struct PostgresClosure *pg;
 
   /**
-   * Transaction status code to set.
+   * Updated to reflect total amount refunded so far.
    */
-  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_Amount refunded_amount;
 
   /**
-   * Plugin context.
+   * Set to the largest refund transaction ID encountered.
    */
-  struct PostgresClosure *pg;
+  uint64_t max_rtransaction_id;
 
+  /**
+   * Set to true on hard errors.
+   */
+  bool err;
 };
 
 
@@ -2234,34 +2011,28 @@ struct GetAuthorizedTipAmountContext
  * Function to be called with the results of a SELECT statement
  * that has returned @a num_results results.
  *
- * @param cls of type `struct GetAuthorizedTipAmountContext *`
+ * @param cls closure, our `struct FindRefundContext`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-find_tip_authorizations_cb (void *cls,
-                            PGresult *result,
-                            unsigned int num_results)
+process_refund_cb (void *cls,
+                   PGresult *result,
+                   unsigned int num_results)
 {
-  struct GetAuthorizedTipAmountContext *ctx = cls;
-  struct PostgresClosure *pg = ctx->pg;
-  unsigned int i;
+  struct FindRefundContext *ictx = cls;
+  struct PostgresClosure *pg = ictx->pg;
 
-  for (i = 0; i < num_results; i++)
+  for (unsigned int i = 0; i<num_results; i++)
   {
-    struct TALER_Amount amount;
-    char *just;
-    json_t *extra;
-    struct GNUNET_HashCode h;
+    /* Sum up existing refunds */
+    struct TALER_Amount acc;
+    uint64_t rtransaction_id;
     struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_string ("justification",
-                                    &just),
-      GNUNET_PQ_result_spec_auto_from_type ("tip_id",
-                                            &h),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                   &amount),
-      TALER_PQ_result_spec_json ("extra",
-                                 &extra),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
+                                   &acc),
+      GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+                                    &rtransaction_id),
       GNUNET_PQ_result_spec_end
     };
 
@@ -2271,111 +2042,85 @@ find_tip_authorizations_cb (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      ictx->err = true;
       return;
     }
-
-    if (0 == i)
-    {
-      ctx->authorized_amount = amount;
-    }
-    else
+    if (0 >
+        TALER_amount_add (&ictx->refunded_amount,
+                          &ictx->refunded_amount,
+                          &acc))
     {
-      if (0 >
-          TALER_amount_add (&ctx->authorized_amount,
-                            &ctx->authorized_amount,
-                            &amount))
-      {
-        GNUNET_break (0);
-        GNUNET_PQ_cleanup_result (rs);
-        ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return;
-      }
+      GNUNET_break (0);
+      ictx->err = true;
+      return;
     }
-    GNUNET_PQ_cleanup_result (rs);
-  }
-
-  if (0 == i)
-  {
-    ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
-  }
-  else
-  {
-    /* one aggregated result */
-    ctx->qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+    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));
   }
 }
 
 
 /**
- * Get the total amount of authorized tips for a tipping reserve.
- *
- * @param cls closure, typically a connection to the db
- * @param reserve_priv which reserve to check
- * @param[out] authorzed_amount amount we've authorized so far for tips
- * @return transaction status, usually
- *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
- *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the reserve_priv
- *      does not identify a known tipping reserve
+ * Closure for #process_deposits_for_refund_cb().
  */
-static enum GNUNET_DB_QueryStatus
-postgres_get_authorized_tip_amount (void *cls,
-                                    const struct
-                                    TALER_ReservePrivateKeyP *reserve_priv,
-                                    struct TALER_Amount *authorized_amount)
+struct InsertRefundContext
 {
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (reserve_priv),
-    GNUNET_PQ_query_param_end
-  };
-  enum GNUNET_DB_QueryStatus qs;
-  struct GetAuthorizedTipAmountContext ctx = {
-    .pg = pg
-  };
+  /**
+   * Used to provide a connection to the db
+   */
+  struct PostgresClosure *pg;
 
-  check_connection (pg);
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "find_tip_authorizations",
-                                             params,
-                                             &find_tip_authorizations_cb,
-                                             &ctx);
-  if (0 >= qs)
-    return qs;
-  *authorized_amount = ctx.authorized_amount;
-  return ctx.qs;
-}
+  /**
+   * 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;
+};
 
 
 /**
- * Closure for #find_payments_cb().
+ * Data extracted per coin.
  */
-struct FindPaymentsContext
+struct RefundCoinData
 {
+
   /**
-   * Function to call with results.
+   * Public key of a coin.
    */
-  TALER_MERCHANTDB_CoinDepositCallback cb;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
 
   /**
-   * Closure for @e cls.
+   * Amount deposited for this coin.
    */
-  void *cb_cls;
+  struct TALER_Amount deposited_with_fee;
 
   /**
-   * Plugin context.
+   * Amount refunded already for this coin.
    */
-  struct PostgresClosure *pg;
+  struct TALER_Amount refund_amount;
 
   /**
-   * Contract term hash used for the search.
+   * Order serial (actually not really per-coin).
    */
-  const struct GNUNET_HashCode *h_contract_terms;
+  uint64_t order_serial;
 
   /**
-   * Transaction status (set).
+   * Maximum rtransaction_id for this coin so far.
    */
-  enum GNUNET_DB_QueryStatus qs;
+  uint64_t max_rtransaction_id;
+
 };
 
 
@@ -2383,419 +2128,549 @@ struct FindPaymentsContext
  * Function to be called with the results of a SELECT statement
  * that has returned @a num_results results.
  *
- * @param cls of type `struct FindPaymentsContext *`
+ * @param cls closure, our `struct InsertRefundContext`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-find_payments_cb (void *cls,
-                  PGresult *result,
-                  unsigned int num_results)
+process_deposits_for_refund_cb (void *cls,
+                                PGresult *result,
+                                unsigned int num_results)
 {
-  struct FindPaymentsContext *fpc = cls;
-  struct PostgresClosure *pg = fpc->pg;
+  struct InsertRefundContext *ctx = cls;
+  struct PostgresClosure *pg = ctx->pg;
+  struct TALER_Amount current_refund;
+  struct RefundCoinData rcd[GNUNET_NZL (num_results)];
 
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_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 TALER_CoinSpendPublicKeyP coin_pub;
-    struct TALER_Amount amount_with_fee;
-    struct TALER_Amount deposit_fee;
-    struct TALER_Amount refund_fee;
-    struct TALER_Amount wire_fee;
-    json_t *exchange_proof;
-    char *exchange_url;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                            &coin_pub),
-      GNUNET_PQ_result_spec_string ("exchange_url",
-                                    &exchange_url),
+                                            &rcd[i].coin_pub),
+      GNUNET_PQ_result_spec_uint64 ("order_serial",
+                                    &rcd[i].order_serial),
       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                   &amount_with_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
-                                   &deposit_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
-                                   &refund_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
-                                   &wire_fee),
-      TALER_PQ_result_spec_json ("exchange_proof",
-                                 &exchange_proof),
+                                   &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_get_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;
+    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 (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
+    if (0 >
+        TALER_amount_add (&current_refund,
+                          &current_refund,
+                          increment))
     {
       GNUNET_break (0);
-      fpc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      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),
+        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;
+        break;
+      case GNUNET_DB_STATUS_SOFT_ERROR:
+        ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
+        break;
+      default:
+        GNUNET_break (0);
+        ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
+        break;
+      }
+      return;
+    }
+
+    /* stop immediately if we are done */
+    if (0 == TALER_amount_cmp (ctx->refund,
+                               &current_refund))
+    {
+      ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
       return;
     }
-    fpc->qs = i + 1;
-    fpc->cb (fpc->cb_cls,
-             fpc->h_contract_terms,
-             &coin_pub,
-             exchange_url,
-             &amount_with_fee,
-             &deposit_fee,
-             &refund_fee,
-             &wire_fee,
-             exchange_proof);
-    GNUNET_PQ_cleanup_result (rs);
   }
+
+  /**
+   * 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;
 }
 
 
 /**
- * Lookup information about coin payments by proposal data hash
- * (and @a merchant_pub)
+ * 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 multiple SQL statements.
  *
  * @param cls closure
- * @param h_contract_terms key for the search
- * @param merchant_pub merchant's public key
- * @param cb function to call with payment data
- * @param cb_cls closure for @a cb
+ * @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 GNUNET_DB_QueryStatus
-postgres_find_payments (void *cls,
-                        const struct GNUNET_HashCode *h_contract_terms,
-                        const struct TALER_MerchantPublicKeyP *merchant_pub,
-                        TALER_MERCHANTDB_CoinDepositCallback cb,
-                        void *cb_cls)
+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_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (order_id),
     GNUNET_PQ_query_param_end
   };
-  struct FindPaymentsContext fpc = {
-    .h_contract_terms = h_contract_terms,
-    .cb = cb,
-    .cb_cls = cb_cls,
-    .pg = pg
+  struct InsertRefundContext ctx = {
+    .pg = pg,
+    .refund = refund,
+    .reason = reason
   };
-  enum GNUNET_DB_QueryStatus qs;
 
-  /* no preflight check here, run in its own transaction by the
-     caller! */
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Finding payment for h_contract_terms '%s'\n",
-              GNUNET_h2s (h_contract_terms));
-  check_connection (pg);
+              "Asked to refund %s on order %s\n",
+              TALER_amount2s (refund),
+              order_id);
   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "find_deposits",
+                                             "find_deposits_for_refund",
                                              params,
-                                             &find_payments_cb,
-                                             &fpc);
-  if (qs <= 0)
-    return qs;
-  return fpc.qs;
+                                             &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 #find_payments_by_coin_cb().
- */
-struct FindPaymentsByCoinContext
-{
-  /**
-   * Function to call with results.
-   */
-  TALER_MERCHANTDB_CoinDepositCallback cb;
-
-  /**
-   * Closure for @e cls.
-   */
-  void *cb_cls;
-
-  /**
-   * Plugin context.
-   */
-  struct PostgresClosure *pg;
-
-  /**
-   * Coin we are looking for.
-   */
-  const struct TALER_CoinSpendPublicKeyP *coin_pub;
-
-  /**
-   * Hash of the contract we are looking for.
-   */
-  const struct GNUNET_HashCode *h_contract_terms;
-
-  /**
-   * Transaction status (set).
-   */
-  enum GNUNET_DB_QueryStatus qs;
-};
-
+/* ********************* OLD API ************************** */
 
 /**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
+ * Retrieve proposal data given its proposal data's hashcode
  *
- * @param cls of type `struct FindPaymentsByCoinContext *`
- * @param result the postgres result
- * @param num_result the number of results in @a result
+ * @param cls closure
+ * @param contract_terms where to store the retrieved proposal data
+ * @param h_contract_terms proposal data's hashcode that will be used to
+ * perform the lookup
+ * @return transaction status
  */
-static void
-find_payments_by_coin_cb (void *cls,
-                          PGresult *result,
-                          unsigned int num_results)
+static enum GNUNET_DB_QueryStatus
+postgres_find_contract_terms_from_hash (
+  void *cls,
+  json_t **contract_terms,
+  const struct GNUNET_HashCode *h_contract_terms,
+  const struct TALER_MerchantPublicKeyP *merchant_pub)
 {
-  struct FindPaymentsByCoinContext *fpc = cls;
-  struct PostgresClosure *pg = fpc->pg;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_Amount amount_with_fee;
-    struct TALER_Amount deposit_fee;
-    struct TALER_Amount refund_fee;
-    struct TALER_Amount wire_fee;
-    char *exchange_url;
-    json_t *exchange_proof;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                   &amount_with_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
-                                   &deposit_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
-                                   &refund_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
-                                   &wire_fee),
-      GNUNET_PQ_result_spec_string ("exchange_url",
-                                    &exchange_url),
-      TALER_PQ_result_spec_json ("exchange_proof",
-                                 &exchange_proof),
-      GNUNET_PQ_result_spec_end
-    };
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_json ("contract_terms",
+                               contract_terms),
+    GNUNET_PQ_result_spec_end
+  };
 
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
-    {
-      GNUNET_break (0);
-      fpc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
-    fpc->qs = i + 1;
-    fpc->cb (fpc->cb_cls,
-             fpc->h_contract_terms,
-             fpc->coin_pub,
-             exchange_url,
-             &amount_with_fee,
-             &deposit_fee,
-             &refund_fee,
-             &wire_fee,
-             exchange_proof);
-    GNUNET_PQ_cleanup_result (rs);
-  }
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   
"find_contract_terms_from_hash",
+                                                   params,
+                                                   rs);
 }
 
 
 /**
- * Retrieve information about a deposited coin.
+ * Retrieve proposal data given its proposal data's hashcode
  *
  * @param cls closure
- * @param h_contract_terms hashcode of the proposal data paid by @a coin_pub
- * @param merchant_pub merchant's public key.
- * @param coin_pub coin's public key used for the search
- * @param cb function to call with payment data
- * @param cb_cls closure for @a cb
+ * @param contract_terms where to store the retrieved proposal data
+ * @param h_contract_terms proposal data's hashcode that will be used to
+ * perform the lookup
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
-postgres_find_payments_by_hash_and_coin (void *cls,
-                                         const struct
-                                         GNUNET_HashCode *h_contract_terms,
-                                         const struct
-                                         TALER_MerchantPublicKeyP 
*merchant_pub,
-                                         const struct
-                                         TALER_CoinSpendPublicKeyP *coin_pub,
-                                         TALER_MERCHANTDB_CoinDepositCallback 
cb,
-                                         void *cb_cls)
+postgres_find_paid_contract_terms_from_hash (void *cls,
+                                             json_t **contract_terms,
+                                             const struct
+                                             GNUNET_HashCode *h_contract_terms,
+                                             const struct
+                                             TALER_MerchantPublicKeyP *
+                                             merchant_pub)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
     GNUNET_PQ_query_param_end
   };
-  struct FindPaymentsByCoinContext fpc = {
-    .cb = cb,
-    .cb_cls = cb_cls,
-    .pg = pg,
-    .h_contract_terms = h_contract_terms,
-    .coin_pub = coin_pub
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_json ("contract_terms",
+                               contract_terms),
+    GNUNET_PQ_result_spec_end
   };
-  enum GNUNET_DB_QueryStatus qs;
 
+  /* no preflight check here, runs in its own transaction from
+     caller (in /pay case) */
   check_connection (pg);
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "find_deposits_by_hash_and_coin",
-                                             params,
-                                             &find_payments_by_coin_cb,
-                                             &fpc);
-  if (0 >= qs)
-    return qs;
-  return fpc.qs;
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   
"find_paid_contract_terms_from_hash",
+                                                   params,
+                                                   rs);
 }
 
 
 /**
- * Closure for #find_transfers_cb().
+ * Store the order ID that was used to pay for a resource within a session.
+ *
+ * @param cls closure
+ * @param session_id session id
+ * @param fulfillment_url URL that canonically identifies the resource
+ *        being paid for
+ * @param order_id the order ID that was used when paying for the resource URL
+ * @param merchant_pub public key of the merchant, identifying the instance
+ * @return transaction status
  */
-struct FindTransfersContext
+enum GNUNET_DB_QueryStatus
+postgres_insert_session_info (void *cls,
+                              const char *session_id,
+                              const char *fulfillment_url,
+                              const char *order_id,
+                              const struct
+                              TALER_MerchantPublicKeyP *merchant_pub)
 {
-  /**
-   * Function to call on results.
-   */
-  TALER_MERCHANTDB_TransferCallback cb;
-
-  /**
-   * Closure for @e cb.
-   */
-  void *cb_cls;
+  struct PostgresClosure *pg = cls;
 
-  /**
-   * Hash of the contract we are looking under.
-   */
-  const struct GNUNET_HashCode *h_contract_terms;
+  struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (session_id),
+    GNUNET_PQ_query_param_string (fulfillment_url),
+    GNUNET_PQ_query_param_string (order_id),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_absolute_time (&now),
+    GNUNET_PQ_query_param_end
+  };
 
-  /**
-   * Transaction status (set).
-   */
-  enum GNUNET_DB_QueryStatus qs;
-};
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_session_info",
+                                             params);
+}
 
 
 /**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results.
+ * Retrieve the order ID that was used to pay for a resource within a session.
  *
- * @param cls of type `struct FindTransfersContext *`
- * @param result the postgres result
- * @param num_result the number of results in @a result
+ * @param cls closure
+ * @param[out] order_id where to store the order ID that was used when
+ *             paying for the resource URL
+ * @param session_id session id
+ * @param fulfillment_url URL that canonically identifies the resource
+ *        being paid for
+ * @param merchant_pub public key of the merchant, identifying the instance
+ * @return transaction status
  */
-static void
-find_transfers_cb (void *cls,
-                   PGresult *result,
-                   unsigned int num_results)
+enum GNUNET_DB_QueryStatus
+postgres_find_session_info (void *cls,
+                            char **order_id,
+                            const char *session_id,
+                            const char *fulfillment_url,
+                            const struct TALER_MerchantPublicKeyP 
*merchant_pub)
 {
-  struct FindTransfersContext *ftc = cls;
-
-  for (unsigned int i = 0; i<num_results; i++)
-  {
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct TALER_WireTransferIdentifierRawP wtid;
-    struct GNUNET_TIME_Absolute execution_time;
-    json_t *proof;
-
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                            &coin_pub),
-      GNUNET_PQ_result_spec_auto_from_type ("wtid",
-                                            &wtid),
-      GNUNET_PQ_result_spec_absolute_time ("execution_time",
-                                           &execution_time),
-      TALER_PQ_result_spec_json ("proof",
-                                 &proof),
-      GNUNET_PQ_result_spec_end
-    };
+  struct PostgresClosure *pg = cls;
 
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
-    {
-      GNUNET_break (0);
-      ftc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
-    ftc->qs = i + 1;
-    ftc->cb (ftc->cb_cls,
-             ftc->h_contract_terms,
-             &coin_pub,
-             &wtid,
-             execution_time,
-             proof);
-    GNUNET_PQ_cleanup_result (rs);
-  }
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (fulfillment_url),
+    GNUNET_PQ_query_param_string (session_id),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_string ("order_id",
+                                  order_id),
+    GNUNET_PQ_result_spec_end
+  };
+  // We don't clean up the result spec since we want
+  // to keep around the memory for order_id.
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "find_session_info",
+                                                   params,
+                                                   rs);
 }
 
 
 /**
- * Lookup information about a transfer by @a h_contract_terms.  Note
- * that in theory there could be multiple wire transfers for a
- * single @a h_contract_terms, as the transaction may have involved
- * multiple coins and the coins may be spread over different wire
- * transfers.
+ * Insert mapping of @a coin_pub and @a h_contract_terms to
+ * corresponding @a wtid.
  *
  * @param cls closure
- * @param h_contract_terms key for the search
- * @param cb function to call with transfer data
- * @param cb_cls closure for @a cb
+ * @param h_contract_terms hashcode of the proposal data paid by @a coin_pub
+ * @param coin_pub public key of the coin
+ * @param wtid identifier of the wire transfer in which the exchange
+ *             send us the money for the coin deposit
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
-postgres_find_transfers_by_hash (void *cls,
+postgres_store_coin_to_transfer (void *cls,
                                  const struct GNUNET_HashCode 
*h_contract_terms,
-                                 TALER_MERCHANTDB_TransferCallback cb,
-                                 void *cb_cls)
+                                 const struct
+                                 TALER_CoinSpendPublicKeyP *coin_pub,
+                                 const struct
+                                 TALER_WireTransferIdentifierRawP *wtid)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_auto_from_type (wtid),
     GNUNET_PQ_query_param_end
   };
-  struct FindTransfersContext ftc = {
-    .h_contract_terms = h_contract_terms,
-    .cb = cb,
-    .cb_cls = cb_cls
-  };
-  enum GNUNET_DB_QueryStatus qs;
 
   check_connection (pg);
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "find_transfers_by_hash",
-                                             params,
-                                             &find_transfers_cb,
-                                             &ftc);
-  if (0 >= qs)
-    return qs;
-  return ftc.qs;
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_transfer",
+                                             params);
 }
 
 
 /**
- * Closure for #find_deposits_cb().
+ * Insert wire transfer confirmation from the exchange into the database.
+ *
+ * @param cls closure
+ * @param exchange_url URL of the exchange
+ * @param wtid identifier of the wire transfer
+ * @param execution_time when was @a wtid executed
+ * @param signkey_pub public key used by the exchange for @a exchange_proof
+ * @param exchange_proof proof from exchange about what the deposit was for
+ * @return transaction status
  */
-struct FindDepositsContext
+static enum GNUNET_DB_QueryStatus
+postgres_store_transfer_to_proof (void *cls,
+                                  const char *exchange_url,
+                                  const struct
+                                  TALER_WireTransferIdentifierRawP *wtid,
+                                  struct GNUNET_TIME_Absolute execution_time,
+                                  const struct
+                                  TALER_ExchangePublicKeyP *signkey_pub,
+                                  const json_t *exchange_proof)
 {
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (exchange_url),
+    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_absolute_time (&execution_time),
+    GNUNET_PQ_query_param_auto_from_type (signkey_pub),
+    TALER_PQ_query_param_json (exchange_proof),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_proof",
+                                             params);
+}
 
+
+/**
+ * Closure for #find_tip_authorizations_cb().
+ */
+struct GetAuthorizedTipAmountContext
+{
   /**
-   * Function to call for each result.
+   * Total authorized amount.
    */
-  TALER_MERCHANTDB_CoinDepositCallback cb;
+  struct TALER_Amount authorized_amount;
 
   /**
-   * Closure for @e cb.
+   * Transaction status code to set.
    */
-  void *cb_cls;
+  enum GNUNET_DB_QueryStatus qs;
 
   /**
    * Plugin context.
    */
   struct PostgresClosure *pg;
 
-  /**
-   * Transaction status (set).
-   */
-  enum GNUNET_DB_QueryStatus qs;
 };
 
 
@@ -2803,45 +2678,34 @@ struct FindDepositsContext
  * Function to be called with the results of a SELECT statement
  * that has returned @a num_results results.
  *
- * @param cls of type `struct FindDepositsContext *`
+ * @param cls of type `struct GetAuthorizedTipAmountContext *`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-find_deposits_cb (void *cls,
-                  PGresult *result,
-                  unsigned int num_results)
+find_tip_authorizations_cb (void *cls,
+                            PGresult *result,
+                            unsigned int num_results)
 {
-  struct FindDepositsContext *fdc = cls;
-  struct PostgresClosure *pg = fdc->pg;
+  struct GetAuthorizedTipAmountContext *ctx = cls;
+  struct PostgresClosure *pg = ctx->pg;
+  unsigned int i;
 
-  for (unsigned int i = 0; i<num_results; i++)
+  for (i = 0; i < num_results; i++)
   {
-    struct GNUNET_HashCode h_contract_terms;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct TALER_Amount amount_with_fee;
-    struct TALER_Amount deposit_fee;
-    struct TALER_Amount refund_fee;
-    struct TALER_Amount wire_fee;
-    char *exchange_url;
-    json_t *exchange_proof;
+    struct TALER_Amount amount;
+    char *just;
+    json_t *extra;
+    struct GNUNET_HashCode h;
     struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                            &h_contract_terms),
-      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                            &coin_pub),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                   &amount_with_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
-                                   &deposit_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
-                                   &refund_fee),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
-                                   &wire_fee),
-      GNUNET_PQ_result_spec_string ("exchange_url",
-                                    &exchange_url),
-      TALER_PQ_result_spec_json ("exchange_proof",
-                                 &exchange_proof),
+      GNUNET_PQ_result_spec_string ("justification",
+                                    &just),
+      GNUNET_PQ_result_spec_auto_from_type ("tip_id",
+                                            &h),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                   &amount),
+      TALER_PQ_result_spec_json ("extra",
+                                 &extra),
       GNUNET_PQ_result_spec_end
     };
 
@@ -2851,78 +2715,96 @@ find_deposits_cb (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      fdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
-    fdc->qs = i + 1;
-    fdc->cb (fdc->cb_cls,
-             &h_contract_terms,
-             &coin_pub,
-             exchange_url,
-             &amount_with_fee,
-             &deposit_fee,
-             &refund_fee,
-             &wire_fee,
-             exchange_proof);
+
+    if (0 == i)
+    {
+      ctx->authorized_amount = amount;
+    }
+    else
+    {
+      if (0 >
+          TALER_amount_add (&ctx->authorized_amount,
+                            &ctx->authorized_amount,
+                            &amount))
+      {
+        GNUNET_break (0);
+        GNUNET_PQ_cleanup_result (rs);
+        ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+        return;
+      }
+    }
     GNUNET_PQ_cleanup_result (rs);
   }
+
+  if (0 == i)
+  {
+    ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  }
+  else
+  {
+    /* one aggregated result */
+    ctx->qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  }
 }
 
 
 /**
- * Lookup information about a coin deposits by @a wtid.
+ * Get the total amount of authorized tips for a tipping reserve.
  *
- * @param cls closure
- * @param wtid wire transfer identifier to find matching transactions for
- * @param cb function to call with payment data
- * @param cb_cls closure for @a cb
- * @return transaction status
+ * @param cls closure, typically a connection to the db
+ * @param reserve_priv which reserve to check
+ * @param[out] authorzed_amount amount we've authorized so far for tips
+ * @return transaction status, usually
+ *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the reserve_priv
+ *      does not identify a known tipping reserve
  */
 static enum GNUNET_DB_QueryStatus
-postgres_find_deposits_by_wtid (void *cls,
-                                const struct
-                                TALER_WireTransferIdentifierRawP *wtid,
-                                TALER_MERCHANTDB_CoinDepositCallback cb,
-                                void *cb_cls)
+postgres_get_authorized_tip_amount (void *cls,
+                                    const struct
+                                    TALER_ReservePrivateKeyP *reserve_priv,
+                                    struct TALER_Amount *authorized_amount)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_auto_from_type (reserve_priv),
     GNUNET_PQ_query_param_end
   };
-  struct FindDepositsContext fdc = {
-    .cb = cb,
-    .cb_cls = cb_cls,
+  enum GNUNET_DB_QueryStatus qs;
+  struct GetAuthorizedTipAmountContext ctx = {
     .pg = pg
   };
-  enum GNUNET_DB_QueryStatus qs;
 
   check_connection (pg);
   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "find_deposits_by_wtid",
+                                             "find_tip_authorizations",
                                              params,
-                                             &find_deposits_cb,
-                                             &fdc);
+                                             &find_tip_authorizations_cb,
+                                             &ctx);
   if (0 >= qs)
     return qs;
-  return fdc.qs;
+  *authorized_amount = ctx.authorized_amount;
+  return ctx.qs;
 }
 
 
 /**
- * Closure for #get_refunds_cb().
+ * Closure for #find_payments_cb().
  */
-struct GetRefundsContext
+struct FindPaymentsContext
 {
   /**
-   * Function to call for each refund.
+   * Function to call with results.
    */
-  TALER_MERCHANTDB_CoinRefundCallback rc;
+  TALER_MERCHANTDB_CoinDepositCallback cb;
 
   /**
-   * Closure for @e rc.
+   * Closure for @e cls.
    */
-  void *rc_cls;
+  void *cb_cls;
 
   /**
    * Plugin context.
@@ -2930,7 +2812,12 @@ struct GetRefundsContext
   struct PostgresClosure *pg;
 
   /**
-   * Transaction result.
+   * Contract term hash used for the search.
+   */
+  const struct GNUNET_HashCode *h_contract_terms;
+
+  /**
+   * Transaction status (set).
    */
   enum GNUNET_DB_QueryStatus qs;
 };
@@ -2940,39 +2827,42 @@ struct GetRefundsContext
  * Function to be called with the results of a SELECT statement
  * that has returned @a num_results results.
  *
- * @param cls of type `struct GetRefundsContext *`
+ * @param cls of type `struct FindPaymentsContext *`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-get_refunds_cb (void *cls,
-                PGresult *result,
-                unsigned int num_results)
+find_payments_cb (void *cls,
+                  PGresult *result,
+                  unsigned int num_results)
 {
-  struct GetRefundsContext *grc = cls;
-  struct PostgresClosure *pg = grc->pg;
+  struct FindPaymentsContext *fpc = cls;
+  struct PostgresClosure *pg = fpc->pg;
 
   for (unsigned int i = 0; i<num_results; i++)
   {
     struct TALER_CoinSpendPublicKeyP coin_pub;
-    uint64_t rtransaction_id;
-    struct TALER_Amount refund_amount;
+    struct TALER_Amount amount_with_fee;
+    struct TALER_Amount deposit_fee;
     struct TALER_Amount refund_fee;
-    char *reason;
+    struct TALER_Amount wire_fee;
+    json_t *exchange_proof;
     char *exchange_url;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
                                             &coin_pub),
       GNUNET_PQ_result_spec_string ("exchange_url",
                                     &exchange_url),
-      GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
-                                    &rtransaction_id),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
-                                   &refund_amount),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                   &amount_with_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
+                                   &deposit_fee),
       TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
                                    &refund_fee),
-      GNUNET_PQ_result_spec_string ("reason",
-                                    &reason),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
+                                   &wire_fee),
+      TALER_PQ_result_spec_json ("exchange_proof",
+                                 &exchange_proof),
       GNUNET_PQ_result_spec_end
     };
 
@@ -2982,330 +2872,248 @@ get_refunds_cb (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      grc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
-    grc->qs = i + 1;
-    grc->rc (grc->rc_cls,
-             &coin_pub,
-             exchange_url,
-             rtransaction_id,
-             reason,
-             &refund_amount,
-             &refund_fee);
-    GNUNET_PQ_cleanup_result (rs);
-  }
-}
-
-
-/**
- * Obtain refunds associated with a contract.
- *
- * @param cls closure, typically a connection to the db
- * @param merchant_pub public key of the merchant instance
- * @param h_contract_terms hash code of the contract
- * @param rc function to call for each coin on which there is a refund
- * @param rc_cls closure for @a rc
- * @return transaction status
- */
-enum GNUNET_DB_QueryStatus
-postgres_get_refunds_from_contract_terms_hash (
-  void *cls,
-  const struct TALER_MerchantPublicKeyP *merchant_pub,
-  const struct GNUNET_HashCode *h_contract_terms,
-  TALER_MERCHANTDB_CoinRefundCallback rc,
-  void *rc_cls)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_end
-  };
-  struct GetRefundsContext grc = {
-    .rc = rc,
-    .rc_cls = rc_cls,
-    .pg = pg
-  };
-  enum GNUNET_DB_QueryStatus qs;
-
-  /* no preflight check here, run in transaction by caller! */
-  TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
-                   GNUNET_h2s (h_contract_terms),
-                   TALER_B2S (merchant_pub));
-  check_connection (pg);
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             
"find_refunds_from_contract_terms_hash",
-                                             params,
-                                             &get_refunds_cb,
-                                             &grc);
-  if (0 >= qs)
-    return qs;
-  return grc.qs;
+      fpc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+    fpc->qs = i + 1;
+    fpc->cb (fpc->cb_cls,
+             fpc->h_contract_terms,
+             &coin_pub,
+             exchange_url,
+             &amount_with_fee,
+             &deposit_fee,
+             &refund_fee,
+             &wire_fee,
+             exchange_proof);
+    GNUNET_PQ_cleanup_result (rs);
+  }
 }
 
 
 /**
- * Obtain refund proofs associated with a refund operation on a
- * coin.
+ * Lookup information about coin payments by proposal data hash
+ * (and @a merchant_pub)
  *
- * @param cls closure, typically a connection to the db
- * @param merchant_pub public key of the merchant instance
- * @param h_contract_terms hash code of the contract
- * @param coin_pub public key of the coin
- * @param rtransaction_id identificator of the refund
- * @param[out] exchange_pub public key of the exchange affirming the refund
- * @param[out] exchange_sig signature of the exchange affirming the refund
+ * @param cls closure
+ * @param h_contract_terms key for the search
+ * @param merchant_pub merchant's public key
+ * @param cb function to call with payment data
+ * @param cb_cls closure for @a cb
  * @return transaction status
  */
-static enum GNUNET_DB_QueryStatus
-postgres_get_refund_proof (
-  void *cls,
-  const struct TALER_MerchantPublicKeyP *merchant_pub,
-  const struct GNUNET_HashCode *h_contract_terms,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
-  uint64_t rtransaction_id,
-  struct TALER_ExchangePublicKeyP *exchange_pub,
-  struct TALER_ExchangeSignatureP *exchange_sig)
+enum GNUNET_DB_QueryStatus
+postgres_find_payments (void *cls,
+                        const struct GNUNET_HashCode *h_contract_terms,
+                        const struct TALER_MerchantPublicKeyP *merchant_pub,
+                        TALER_MERCHANTDB_CoinDepositCallback cb,
+                        void *cb_cls)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
-    GNUNET_PQ_query_param_uint64 (&rtransaction_id),
     GNUNET_PQ_query_param_end
   };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
-                                          exchange_sig),
-    GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
-                                          exchange_pub),
-    GNUNET_PQ_result_spec_end
+  struct FindPaymentsContext fpc = {
+    .h_contract_terms = h_contract_terms,
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .pg = pg
   };
+  enum GNUNET_DB_QueryStatus qs;
 
+  /* no preflight check here, run in its own transaction by the
+     caller! */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Finding payment for h_contract_terms '%s'\n",
+              GNUNET_h2s (h_contract_terms));
   check_connection (pg);
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "get_refund_proof",
-                                                   params,
-                                                   rs);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "find_deposits",
+                                             params,
+                                             &find_payments_cb,
+                                             &fpc);
+  if (qs <= 0)
+    return qs;
+  return fpc.qs;
 }
 
 
 /**
- * Store refund proofs associated with a refund operation on a
- * coin.
- *
- * @param cls closure, typically a connection to the db
- * @param merchant_pub public key of the merchant instance
- * @param h_contract_terms hash code of the contract
- * @param coin_pub public key of the coin
- * @param rtransaction_id identificator of the refund
- * @param exchange_pub public key of the exchange affirming the refund
- * @param exchange_sig signature of the exchange affirming the refund
- * @return transaction status
+ * Closure for #find_payments_by_coin_cb().
  */
-static enum GNUNET_DB_QueryStatus
-postgres_put_refund_proof (
-  void *cls,
-  const struct TALER_MerchantPublicKeyP *merchant_pub,
-  const struct GNUNET_HashCode *h_contract_terms,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
-  uint64_t rtransaction_id,
-  const struct TALER_ExchangePublicKeyP *exchange_pub,
-  const struct TALER_ExchangeSignatureP *exchange_sig)
+struct FindPaymentsByCoinContext
 {
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_uint64 (&rtransaction_id),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
-    GNUNET_PQ_query_param_auto_from_type (exchange_sig),
-    GNUNET_PQ_query_param_auto_from_type (exchange_pub),
-    GNUNET_PQ_query_param_end
-  };
+  /**
+   * Function to call with results.
+   */
+  TALER_MERCHANTDB_CoinDepositCallback cb;
 
-  TALER_LOG_DEBUG ("Inserting refund proof %s + %s\n",
-                   GNUNET_h2s (h_contract_terms),
-                   TALER_B2S (coin_pub));
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_refund_proof",
-                                             params);
-}
+  /**
+   * Closure for @e cls.
+   */
+  void *cb_cls;
 
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
 
-/**
- * Insert a refund row into merchant_refunds.  Not meant to be exported
- * in the db API.
- *
- * @param cls closure, typically a connection to the db
- * @param merchant_pub merchant instance public key
- * @param h_contract_terms hashcode of the contract related to this refund
- * @param coin_pub public key of the coin giving the (part of) refund
- * @param reason human readable explanation behind the refund
- * @param refund how much this coin is refunding
- */
-static enum GNUNET_DB_QueryStatus
-insert_refund (void *cls,
-               const struct TALER_MerchantPublicKeyP *merchant_pub,
-               const struct GNUNET_HashCode *h_contract_terms,
-               const struct TALER_CoinSpendPublicKeyP *coin_pub,
-               const char *reason,
-               const struct TALER_Amount *refund)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
-    GNUNET_PQ_query_param_string (reason),
-    TALER_PQ_query_param_amount (refund),
-    GNUNET_PQ_query_param_end
-  };
+  /**
+   * Coin we are looking for.
+   */
+  const struct TALER_CoinSpendPublicKeyP *coin_pub;
 
-  TALER_LOG_DEBUG ("Inserting refund %s + %s\n",
-                   GNUNET_h2s (h_contract_terms),
-                   TALER_B2S (merchant_pub));
+  /**
+   * Hash of the contract we are looking for.
+   */
+  const struct GNUNET_HashCode *h_contract_terms;
 
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_refund",
-                                             params);
-}
+  /**
+   * Transaction status (set).
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
 
 
 /**
- * Store information about wire fees charged by an exchange,
- * including signature (so we have proof).
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
  *
- * @param cls closure
- * @paramm exchange_pub public key of the exchange
- * @param h_wire_method hash of wire method
- * @param wire_fee wire fee charged
- * @param closing_fee closing fee charged (irrelevant for us,
- *              but needed to check signature)
- * @param start_date start of fee being used
- * @param end_date end of fee being used
- * @param exchange_sig signature of exchange over fee structure
- * @return transaction status code
+ * @param cls of type `struct FindPaymentsByCoinContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
  */
-static enum GNUNET_DB_QueryStatus
-postgres_store_wire_fee_by_exchange (
-  void *cls,
-  const struct
-  TALER_MasterPublicKeyP *exchange_pub,
-  const struct
-  GNUNET_HashCode *h_wire_method,
-  const struct TALER_Amount *wire_fee,
-  const struct TALER_Amount *closing_fee,
-  struct GNUNET_TIME_Absolute start_date,
-  struct GNUNET_TIME_Absolute end_date,
-  const struct
-  TALER_MasterSignatureP *exchange_sig)
+static void
+find_payments_by_coin_cb (void *cls,
+                          PGresult *result,
+                          unsigned int num_results)
 {
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (exchange_pub),
-    GNUNET_PQ_query_param_auto_from_type (h_wire_method),
-    TALER_PQ_query_param_amount (wire_fee),
-    TALER_PQ_query_param_amount (closing_fee),
-    GNUNET_PQ_query_param_absolute_time (&start_date),
-    GNUNET_PQ_query_param_absolute_time (&end_date),
-    GNUNET_PQ_query_param_auto_from_type (exchange_sig),
-    GNUNET_PQ_query_param_end
-  };
+  struct FindPaymentsByCoinContext *fpc = cls;
+  struct PostgresClosure *pg = fpc->pg;
 
-  /* 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 (exchange_pub),
-              GNUNET_STRINGS_absolute_time_to_string (start_date),
-              TALER_amount2s (wire_fee));
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_wire_fee",
-                                             params);
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_Amount amount_with_fee;
+    struct TALER_Amount deposit_fee;
+    struct TALER_Amount refund_fee;
+    struct TALER_Amount wire_fee;
+    char *exchange_url;
+    json_t *exchange_proof;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                   &amount_with_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
+                                   &deposit_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
+                                   &refund_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
+                                   &wire_fee),
+      GNUNET_PQ_result_spec_string ("exchange_url",
+                                    &exchange_url),
+      TALER_PQ_result_spec_json ("exchange_proof",
+                                 &exchange_proof),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      fpc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+    fpc->qs = i + 1;
+    fpc->cb (fpc->cb_cls,
+             fpc->h_contract_terms,
+             fpc->coin_pub,
+             exchange_url,
+             &amount_with_fee,
+             &deposit_fee,
+             &refund_fee,
+             &wire_fee,
+             exchange_proof);
+    GNUNET_PQ_cleanup_result (rs);
+  }
 }
 
 
 /**
- * Obtain information about wire fees charged by an exchange,
- * including signature (so we have proof).
+ * Retrieve information about a deposited coin.
  *
  * @param cls closure
- * @param exchange_pub public key of the exchange
- * @param h_wire_method hash of wire method
- * @param contract_date date of the contract to use for the lookup
- * @param[out] wire_fee wire fee charged
- * @param[out] closing_fee closing fee charged (irrelevant for us,
- *              but needed to check signature)
- * @param[out] start_date start of fee being used
- * @param[out] end_date end of fee being used
- * @param[out] exchange_sig signature of exchange over fee structure
- * @return transaction status code
+ * @param h_contract_terms hashcode of the proposal data paid by @a coin_pub
+ * @param merchant_pub merchant's public key.
+ * @param coin_pub coin's public key used for the search
+ * @param cb function to call with payment data
+ * @param cb_cls closure for @a cb
+ * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
-postgres_lookup_wire_fee (void *cls,
-                          const struct TALER_MasterPublicKeyP *exchange_pub,
-                          const struct GNUNET_HashCode *h_wire_method,
-                          struct GNUNET_TIME_Absolute contract_date,
-                          struct TALER_Amount *wire_fee,
-                          struct TALER_Amount *closing_fee,
-                          struct GNUNET_TIME_Absolute *start_date,
-                          struct GNUNET_TIME_Absolute *end_date,
-                          struct TALER_MasterSignatureP *exchange_sig)
+postgres_find_payments_by_hash_and_coin (void *cls,
+                                         const struct
+                                         GNUNET_HashCode *h_contract_terms,
+                                         const struct
+                                         TALER_MerchantPublicKeyP 
*merchant_pub,
+                                         const struct
+                                         TALER_CoinSpendPublicKeyP *coin_pub,
+                                         TALER_MERCHANTDB_CoinDepositCallback 
cb,
+                                         void *cb_cls)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (exchange_pub),
-    GNUNET_PQ_query_param_auto_from_type (h_wire_method),
-    GNUNET_PQ_query_param_absolute_time (&contract_date),
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
     GNUNET_PQ_query_param_end
   };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
-                                 wire_fee),
-    TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
-                                 closing_fee),
-    GNUNET_PQ_result_spec_absolute_time ("start_date",
-                                         start_date),
-    GNUNET_PQ_result_spec_absolute_time ("end_date",
-                                         end_date),
-    GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
-                                          exchange_sig),
-    GNUNET_PQ_result_spec_end
+  struct FindPaymentsByCoinContext fpc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .pg = pg,
+    .h_contract_terms = h_contract_terms,
+    .coin_pub = coin_pub
   };
+  enum GNUNET_DB_QueryStatus qs;
 
   check_connection (pg);
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "lookup_wire_fee",
-                                                   params,
-                                                   rs);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "find_deposits_by_hash_and_coin",
+                                             params,
+                                             &find_payments_by_coin_cb,
+                                             &fpc);
+  if (0 >= qs)
+    return qs;
+  return fpc.qs;
 }
 
 
 /**
- * Closure for #process_refund_cb.
+ * Closure for #find_transfers_cb().
  */
-struct FindRefundContext
+struct FindTransfersContext
 {
+  /**
+   * Function to call on results.
+   */
+  TALER_MERCHANTDB_TransferCallback cb;
 
   /**
-   * Plugin context.
+   * Closure for @e cb.
    */
-  struct PostgresClosure *pg;
+  void *cb_cls;
 
   /**
-   * Updated to reflect total amount refunded so far.
+   * Hash of the contract we are looking under.
    */
-  struct TALER_Amount refunded_amount;
+  const struct GNUNET_HashCode *h_contract_terms;
 
   /**
-   * Set to #GNUNET_SYSERR on hard errors.
+   * Transaction status (set).
    */
-  int err;
+  enum GNUNET_DB_QueryStatus qs;
 };
 
 
@@ -3313,25 +3121,33 @@ struct FindRefundContext
  * 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 cls of type `struct FindTransfersContext *`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-process_refund_cb (void *cls,
+find_transfers_cb (void *cls,
                    PGresult *result,
                    unsigned int num_results)
 {
-  struct FindRefundContext *ictx = cls;
-  struct PostgresClosure *pg = ictx->pg;
+  struct FindTransfersContext *ftc = cls;
 
   for (unsigned int i = 0; i<num_results; i++)
   {
-    /* Sum up existing refunds */
-    struct TALER_Amount acc;
+    struct TALER_CoinSpendPublicKeyP coin_pub;
+    struct TALER_WireTransferIdentifierRawP wtid;
+    struct GNUNET_TIME_Absolute execution_time;
+    json_t *proof;
+
     struct GNUNET_PQ_ResultSpec rs[] = {
-      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
-                                   &acc),
+      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+                                            &coin_pub),
+      GNUNET_PQ_result_spec_auto_from_type ("wtid",
+                                            &wtid),
+      GNUNET_PQ_result_spec_absolute_time ("execution_time",
+                                           &execution_time),
+      TALER_PQ_result_spec_json ("proof",
+                                 &proof),
       GNUNET_PQ_result_spec_end
     };
 
@@ -3341,57 +3157,224 @@ process_refund_cb (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      ictx->err = GNUNET_SYSERR;
+      ftc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
-    if (0 >
-        TALER_amount_add (&ictx->refunded_amount,
-                          &ictx->refunded_amount,
-                          &acc))
+    ftc->qs = i + 1;
+    ftc->cb (ftc->cb_cls,
+             ftc->h_contract_terms,
+             &coin_pub,
+             &wtid,
+             execution_time,
+             proof);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+/**
+ * Lookup information about a transfer by @a h_contract_terms.  Note
+ * that in theory there could be multiple wire transfers for a
+ * single @a h_contract_terms, as the transaction may have involved
+ * multiple coins and the coins may be spread over different wire
+ * transfers.
+ *
+ * @param cls closure
+ * @param h_contract_terms key for the search
+ * @param cb function to call with transfer data
+ * @param cb_cls closure for @a cb
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_find_transfers_by_hash (void *cls,
+                                 const struct GNUNET_HashCode 
*h_contract_terms,
+                                 TALER_MERCHANTDB_TransferCallback cb,
+                                 void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_end
+  };
+  struct FindTransfersContext ftc = {
+    .h_contract_terms = h_contract_terms,
+    .cb = cb,
+    .cb_cls = cb_cls
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "find_transfers_by_hash",
+                                             params,
+                                             &find_transfers_cb,
+                                             &ftc);
+  if (0 >= qs)
+    return qs;
+  return ftc.qs;
+}
+
+
+/**
+ * Closure for #find_deposits_cb().
+ */
+struct FindDepositsContext
+{
+
+  /**
+   * Function to call for each result.
+   */
+  TALER_MERCHANTDB_CoinDepositCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Transaction status (set).
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls of type `struct FindDepositsContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+find_deposits_cb (void *cls,
+                  PGresult *result,
+                  unsigned int num_results)
+{
+  struct FindDepositsContext *fdc = cls;
+  struct PostgresClosure *pg = fdc->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct GNUNET_HashCode h_contract_terms;
+    struct TALER_CoinSpendPublicKeyP coin_pub;
+    struct TALER_Amount amount_with_fee;
+    struct TALER_Amount deposit_fee;
+    struct TALER_Amount refund_fee;
+    struct TALER_Amount wire_fee;
+    char *exchange_url;
+    json_t *exchange_proof;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                            &h_contract_terms),
+      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+                                            &coin_pub),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+                                   &amount_with_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
+                                   &deposit_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
+                                   &refund_fee),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
+                                   &wire_fee),
+      GNUNET_PQ_result_spec_string ("exchange_url",
+                                    &exchange_url),
+      TALER_PQ_result_spec_json ("exchange_proof",
+                                 &exchange_proof),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
     {
       GNUNET_break (0);
-      ictx->err = GNUNET_SYSERR;
+      fdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Found refund of %s\n",
-                TALER_amount2s (&acc));
+    fdc->qs = i + 1;
+    fdc->cb (fdc->cb_cls,
+             &h_contract_terms,
+             &coin_pub,
+             exchange_url,
+             &amount_with_fee,
+             &deposit_fee,
+             &refund_fee,
+             &wire_fee,
+             exchange_proof);
+    GNUNET_PQ_cleanup_result (rs);
   }
 }
 
 
 /**
- * Closure for #process_deposits_for_refund_cb.
+ * Lookup information about a coin deposits by @a wtid.
+ *
+ * @param cls closure
+ * @param wtid wire transfer identifier to find matching transactions for
+ * @param cb function to call with payment data
+ * @param cb_cls closure for @a cb
+ * @return transaction status
  */
-struct InsertRefundContext
+static enum GNUNET_DB_QueryStatus
+postgres_find_deposits_by_wtid (void *cls,
+                                const struct
+                                TALER_WireTransferIdentifierRawP *wtid,
+                                TALER_MERCHANTDB_CoinDepositCallback cb,
+                                void *cb_cls)
 {
-  /**
-   * Used to provide a connection to the db
-   */
-  struct PostgresClosure *pg;
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_end
+  };
+  struct FindDepositsContext fdc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .pg = pg
+  };
+  enum GNUNET_DB_QueryStatus qs;
 
-  /**
-   * Amount to which increase the refund for this contract
-   */
-  const struct TALER_Amount *refund;
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "find_deposits_by_wtid",
+                                             params,
+                                             &find_deposits_cb,
+                                             &fdc);
+  if (0 >= qs)
+    return qs;
+  return fdc.qs;
+}
 
+
+/**
+ * Closure for #get_refunds_cb().
+ */
+struct GetRefundsContext
+{
   /**
-   * Merchant instance public key
+   * Function to call for each refund.
    */
-  const struct TALER_MerchantPublicKeyP *merchant_pub;
+  TALER_MERCHANTDB_CoinRefundCallback rc;
 
   /**
-   * Hash code representing the contract
+   * Closure for @e rc.
    */
-  const struct GNUNET_HashCode *h_contract_terms;
+  void *rc_cls;
 
   /**
-   * Human-readable reason behind this refund
+   * Plugin context.
    */
-  const char *reason;
+  struct PostgresClosure *pg;
 
   /**
-   * Transaction status code.
+   * Transaction result.
    */
   enum GNUNET_DB_QueryStatus qs;
 };
@@ -3401,48 +3384,41 @@ struct InsertRefundContext
  * 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 cls of type `struct GetRefundsContext *`
  * @param result the postgres result
  * @param num_result the number of results in @a result
  */
 static void
-process_deposits_for_refund_cb (void *cls,
-                                PGresult *result,
-                                unsigned int num_results)
+get_refunds_cb (void *cls,
+                PGresult *result,
+                unsigned int num_results)
 {
-  struct InsertRefundContext *ctx = cls;
-  struct PostgresClosure *pg = ctx->pg;
-  struct TALER_Amount current_refund;
-  struct TALER_Amount deposit_refund[GNUNET_NZL (num_results)];
-  struct TALER_CoinSpendPublicKeyP deposit_coin_pubs[GNUNET_NZL (num_results)];
-  struct TALER_Amount deposit_amount_with_fee[GNUNET_NZL (num_results)];
-
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (ctx->refund->currency,
-                                        &current_refund));
+  struct GetRefundsContext *grc = cls;
+  struct PostgresClosure *pg = grc->pg;
 
-  /* 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 TALER_CoinSpendPublicKeyP coin_pub;
-    struct TALER_Amount amount_with_fee;
+    uint64_t rtransaction_id;
+    struct TALER_Amount refund_amount;
+    struct TALER_Amount refund_fee;
+    char *reason;
+    char *exchange_url;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
                                             &coin_pub),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                   &amount_with_fee),
+      GNUNET_PQ_result_spec_string ("exchange_url",
+                                    &exchange_url),
+      GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+                                    &rtransaction_id),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
+                                   &refund_amount),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
+                                   &refund_fee),
+      GNUNET_PQ_result_spec_string ("reason",
+                                    &reason),
       GNUNET_PQ_result_spec_end
     };
-    struct FindRefundContext ictx = {
-      .err = GNUNET_OK,
-      .pg = pg
-    };
-    enum GNUNET_DB_QueryStatus ires;
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (&coin_pub),
-      GNUNET_PQ_query_param_end
-    };
 
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
@@ -3450,232 +3426,267 @@ process_deposits_for_refund_cb (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (ctx->refund->currency,
-                                          &ictx.refunded_amount));
-    ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
-                                                 "find_refunds",
-                                                 params,
-                                                 &process_refund_cb,
-                                                 &ictx);
-    if ( (GNUNET_OK != ictx.err) ||
-         (GNUNET_DB_STATUS_HARD_ERROR == ires) )
-    {
-      GNUNET_break (0);
-      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
-    if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
-    {
-      ctx->qs = GNUNET_DB_STATUS_SOFT_ERROR;
-      return;
-    }
-    deposit_refund[i] = ictx.refunded_amount;
-    deposit_amount_with_fee[i] = amount_with_fee;
-    deposit_coin_pubs[i] = coin_pub;
-    if (0 >
-        TALER_amount_add (&current_refund,
-                          &current_refund,
-                          &ictx.refunded_amount))
-    {
-      GNUNET_break (0);
-      ctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      grc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Existing refund for coin %s is %s\n",
-                TALER_B2S (&coin_pub),
-                TALER_amount2s (&ictx.refunded_amount));
+    grc->qs = i + 1;
+    grc->rc (grc->rc_cls,
+             &coin_pub,
+             exchange_url,
+             rtransaction_id,
+             reason,
+             &refund_amount,
+             &refund_fee);
+    GNUNET_PQ_cleanup_result (rs);
   }
+}
 
-  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));
-    return;
-  }
+/**
+ * Obtain refunds associated with a contract.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param merchant_pub public key of the merchant instance
+ * @param h_contract_terms hash code of the contract
+ * @param rc function to call for each coin on which there is a refund
+ * @param rc_cls closure for @a rc
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+postgres_get_refunds_from_contract_terms_hash (
+  void *cls,
+  const struct TALER_MerchantPublicKeyP *merchant_pub,
+  const struct GNUNET_HashCode *h_contract_terms,
+  TALER_MERCHANTDB_CoinRefundCallback rc,
+  void *rc_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_end
+  };
+  struct GetRefundsContext grc = {
+    .rc = rc,
+    .rc_cls = rc_cls,
+    .pg = pg
+  };
+  enum GNUNET_DB_QueryStatus qs;
 
-  /* 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;
+  /* no preflight check here, run in transaction by caller! */
+  TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
+                   GNUNET_h2s (h_contract_terms),
+                   TALER_B2S (merchant_pub));
+  check_connection (pg);
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             
"find_refunds_from_contract_terms_hash",
+                                             params,
+                                             &get_refunds_cb,
+                                             &grc);
+  if (0 >= qs)
+    return qs;
+  return grc.qs;
+}
 
-    /* How much of the coin is left after the existing refunds? */
-    if (0 >
-        TALER_amount_subtract (&left,
-                               &deposit_amount_with_fee[i],
-                               &deposit_refund[i]))
-    {
-      GNUNET_break (0);
-      ctx->qs = GNUNET_DB_STATUS_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 (&deposit_coin_pubs[i]));
-      continue;
-    }
+/**
+ * Obtain refund proofs associated with a refund operation on a
+ * coin.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param merchant_pub public key of the merchant instance
+ * @param h_contract_terms hash code of the contract
+ * @param coin_pub public key of the coin
+ * @param rtransaction_id identificator of the refund
+ * @param[out] exchange_pub public key of the exchange affirming the refund
+ * @param[out] exchange_sig signature of the exchange affirming the refund
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_refund_proof (
+  void *cls,
+  const struct TALER_MerchantPublicKeyP *merchant_pub,
+  const struct GNUNET_HashCode *h_contract_terms,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  uint64_t rtransaction_id,
+  struct TALER_ExchangePublicKeyP *exchange_pub,
+  struct TALER_ExchangeSignatureP *exchange_sig)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_uint64 (&rtransaction_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
+                                          exchange_sig),
+    GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
+                                          exchange_pub),
+    GNUNET_PQ_result_spec_end
+  };
 
-    /* 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->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "get_refund_proof",
+                                                   params,
+                                                   rs);
+}
 
-    /* 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->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      return;
-    }
+/**
+ * Store refund proofs associated with a refund operation on a
+ * coin.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param merchant_pub public key of the merchant instance
+ * @param h_contract_terms hash code of the contract
+ * @param coin_pub public key of the coin
+ * @param rtransaction_id identificator of the refund
+ * @param exchange_pub public key of the exchange affirming the refund
+ * @param exchange_sig signature of the exchange affirming the refund
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_put_refund_proof (
+  void *cls,
+  const struct TALER_MerchantPublicKeyP *merchant_pub,
+  const struct GNUNET_HashCode *h_contract_terms,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  uint64_t rtransaction_id,
+  const struct TALER_ExchangePublicKeyP *exchange_pub,
+  const struct TALER_ExchangeSignatureP *exchange_sig)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&rtransaction_id),
+    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_auto_from_type (exchange_sig),
+    GNUNET_PQ_query_param_auto_from_type (exchange_pub),
+    GNUNET_PQ_query_param_end
+  };
 
-    /* actually run the refund */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Coin %s deposit amount is %s\n",
-                TALER_B2S (&deposit_coin_pubs[i]),
-                TALER_amount2s (&deposit_amount_with_fee[i]));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Coin %s refund will be incremented by %s\n",
-                TALER_B2S (&deposit_coin_pubs[i]),
-                TALER_amount2s (increment));
-    {
-      enum GNUNET_DB_QueryStatus qs;
+  TALER_LOG_DEBUG ("Inserting refund proof %s + %s\n",
+                   GNUNET_h2s (h_contract_terms),
+                   TALER_B2S (coin_pub));
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_refund_proof",
+                                             params);
+}
 
-      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          (qs = insert_refund (ctx->pg,
-                               ctx->merchant_pub,
-                               ctx->h_contract_terms,
-                               &deposit_coin_pubs[i],
-                               ctx->reason,
-                               increment)))
-      {
-        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-        ctx->qs = qs;
-        return;
-      }
-    }
 
-    /* stop immediately if we are done */
-    if (0 == TALER_amount_cmp (ctx->refund,
-                               &current_refund))
-      return;
-  }
+/**
+ * Store information about wire fees charged by an exchange,
+ * including signature (so we have proof).
+ *
+ * @param cls closure
+ * @paramm exchange_pub public key of the exchange
+ * @param h_wire_method hash of wire method
+ * @param wire_fee wire fee charged
+ * @param closing_fee closing fee charged (irrelevant for us,
+ *              but needed to check signature)
+ * @param start_date start of fee being used
+ * @param end_date end of fee being used
+ * @param exchange_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 *exchange_pub,
+  const struct
+  GNUNET_HashCode *h_wire_method,
+  const struct TALER_Amount *wire_fee,
+  const struct TALER_Amount *closing_fee,
+  struct GNUNET_TIME_Absolute start_date,
+  struct GNUNET_TIME_Absolute end_date,
+  const struct
+  TALER_MasterSignatureP *exchange_sig)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (exchange_pub),
+    GNUNET_PQ_query_param_auto_from_type (h_wire_method),
+    TALER_PQ_query_param_amount (wire_fee),
+    TALER_PQ_query_param_amount (closing_fee),
+    GNUNET_PQ_query_param_absolute_time (&start_date),
+    GNUNET_PQ_query_param_absolute_time (&end_date),
+    GNUNET_PQ_query_param_auto_from_type (exchange_sig),
+    GNUNET_PQ_query_param_end
+  };
 
-  /**
-   * 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->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  /* 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 (exchange_pub),
+              GNUNET_STRINGS_absolute_time_to_string (start_date),
+              TALER_amount2s (wire_fee));
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_wire_fee",
+                                             params);
 }
 
 
 /**
- * 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 multiple SQL statements (NT).
+ * Obtain information about wire fees charged by an exchange,
+ * including signature (so we have proof).
  *
  * @param cls closure
- * @param h_contract_terms
- * @param merchant_pub merchant's instance public key
- * @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!)
+ * @param exchange_pub public key of the exchange
+ * @param h_wire_method hash of wire method
+ * @param contract_date date of the contract to use for the lookup
+ * @param[out] wire_fee wire fee charged
+ * @param[out] closing_fee closing fee charged (irrelevant for us,
+ *              but needed to check signature)
+ * @param[out] start_date start of fee being used
+ * @param[out] end_date end of fee being used
+ * @param[out] exchange_sig signature of exchange over fee structure
+ * @return transaction status code
  */
 static enum GNUNET_DB_QueryStatus
-postgres_increase_refund_for_contract_NT (
-  void *cls,
-  const struct GNUNET_HashCode *h_contract_terms,
-  const struct TALER_MerchantPublicKeyP *merchant_pub,
-  const struct TALER_Amount *refund,
-  const char *reason)
+postgres_lookup_wire_fee (void *cls,
+                          const struct TALER_MasterPublicKeyP *exchange_pub,
+                          const struct GNUNET_HashCode *h_wire_method,
+                          struct GNUNET_TIME_Absolute contract_date,
+                          struct TALER_Amount *wire_fee,
+                          struct TALER_Amount *closing_fee,
+                          struct GNUNET_TIME_Absolute *start_date,
+                          struct GNUNET_TIME_Absolute *end_date,
+                          struct TALER_MasterSignatureP *exchange_sig)
 {
   struct PostgresClosure *pg = cls;
-  enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-    GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+    GNUNET_PQ_query_param_auto_from_type (exchange_pub),
+    GNUNET_PQ_query_param_auto_from_type (h_wire_method),
+    GNUNET_PQ_query_param_absolute_time (&contract_date),
     GNUNET_PQ_query_param_end
   };
-  struct InsertRefundContext ctx = {
-    .pg = pg,
-    .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT,
-    .refund = refund,
-    .reason = reason,
-    .h_contract_terms = h_contract_terms,
-    .merchant_pub = merchant_pub
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
+                                 wire_fee),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
+                                 closing_fee),
+    GNUNET_PQ_result_spec_absolute_time ("start_date",
+                                         start_date),
+    GNUNET_PQ_result_spec_absolute_time ("end_date",
+                                         end_date),
+    GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
+                                          exchange_sig),
+    GNUNET_PQ_result_spec_end
   };
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Asked to refund %s on contract %s\n",
-              TALER_amount2s (refund),
-              GNUNET_h2s (h_contract_terms));
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "find_deposits",
-                                             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 GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
-  case GNUNET_DB_STATUS_SOFT_ERROR:
-  case GNUNET_DB_STATUS_HARD_ERROR:
-    return qs;
-  default:
-    /* Got one or more deposits */
-    return ctx.qs;
-  }
+  check_connection (pg);
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "lookup_wire_fee",
+                                                   params,
+                                                   rs);
 }
 
 
@@ -5527,6 +5538,14 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
                             "        WHERE merchant_id=$1)"
                             "   AND order_id=$2",
                             2),
+    GNUNET_PQ_make_prepare ("find_refunds_by_coin",
+                            "SELECT"
+                            " refund_amount_val"
+                            ",refund_amount_frac"
+                            " FROM merchant_refunds"
+                            " WHERE coin_pub=$1"
+                            "   AND order_serial=$2",
+                            2),
 
     /* OLD API: */
 
@@ -5926,7 +5945,9 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->mark_contract_paid = &postgres_mark_contract_paid;
   plugin->refund_coin = &postgres_refund_coin;
   plugin->lookup_order_status = &postgres_lookup_order_status;
-  /* OLD API: */
+  plugin->increase_refund = &postgres_increase_refund;
+
+/* OLD API: */
   plugin->find_contract_terms_from_hash =
     &postgres_find_contract_terms_from_hash;
   plugin->find_paid_contract_terms_from_hash =
@@ -5941,8 +5962,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid;
   plugin->get_authorized_tip_amount = &postgres_get_authorized_tip_amount;
   plugin->lookup_wire_fee = &postgres_lookup_wire_fee;
-  plugin->increase_refund_for_contract_NT =
-    &postgres_increase_refund_for_contract_NT;
   plugin->get_refund_proof = &postgres_get_refund_proof;
   plugin->put_refund_proof = &postgres_put_refund_proof;
   plugin->insert_session_info = &postgres_insert_session_info;
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index f5396a4..d2e7801 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -335,6 +335,40 @@ typedef void
   const struct TALER_Amount *refund_amount);
 
 
+/**
+ * Results from trying to increase a refund.
+ */
+enum TALER_MERCHANTDB_RefundStatus
+{
+
+  /**
+   * Refund amount exceeds original payment.
+   */
+  TALER_MERCHANTDB_RS_TOO_HIGH = -3,
+
+  /**
+   * Hard database failure.
+   */
+  TALER_MERCHANTDB_RS_HARD_ERROR = -2,
+
+  /**
+   * Soft database failure.
+   */
+  TALER_MERCHANTDB_RS_SOFT_ERROR = -1,
+
+  /**
+   * Order not found.
+   */
+  TALER_MERCHANTDB_RS_NO_SUCH_ORDER = 0,
+
+  /**
+   * Refund is now at or above the requested amount.
+   */
+  TALER_MERCHANTDB_RS_SUCCESS = 1
+
+};
+
+
 /* **************** OLD: ******************** */
 
 /**
@@ -461,40 +495,6 @@ typedef void
   const struct TALER_Amount *refund_fee);
 
 
-/**
- * Results from trying to increase a refund.
- */
-enum TALER_MERCHANTDB_RefundStatus
-{
-
-  /**
-   * Refund amount exceeds original payment.
-   */
-  TALER_MERCHANTDB_RS_TOO_HIGH = -3,
-
-  /**
-   * Hard database failure.
-   */
-  TALER_MERCHANTDB_RS_HARD_ERROR = -2,
-
-  /**
-   * Soft database failure.
-   */
-  TALER_MERCHANTDB_RS_SOFT_ERROR = -1,
-
-  /**
-   * Order not found.
-   */
-  TALER_MERCHANTDB_RS_NO_SUCH_ORDER = 0,
-
-  /**
-   * Refund is now at or above the requested amount.
-   */
-  TALER_MERCHANTDB_RS_SUCCESS = 1
-
-};
-
-
 /**
  * Handle to interact with the database.
  *

-- 
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]